一、数据类型
常量
1、通过预处理声明常量
#include <stdio.h>#define PRICE 100int main() { printf("价格:%d\n",PRICE); return 0; }
2、通过 const 关键字声明常量
#include <stdio.h>const int NUM=10;int main() { printf("数量:%d\n",NUM); return 0; }
区别:#define 在运行时会替换为指定的值,const可以看到数据类型,更推荐使用 const
整型
在C语言中分为有符号和无符号整型,无符号是整数,有符号包含负数
类型 | 说明 |
---|---|
short | 2字节 |
int | 4字节 |
long | 32位系统4字节,64位系统8字节 |
long long | 8字节 |
int main() { int a=10; int b=-10; long long c=20; int d=0b111;//二进制 int e=0xb;//十六进制 int f=010;//八进制 unsigned int g=12;//无符号正数 printf("a=%d,b=%d,c=%d,d=%d",a,b,c,d); return 0; }
0b 为二进制方式赋值
0x 为十六进制数据
0 为八进制数据
进制相关知识参考其他教程
c11 标准 stdint.h 对数据的长度进行了统一
类型 | 说明 |
---|---|
int8_t | 统一8位1字节 |
int16_t | 2字节 |
int32_t | 4字节 |
int64_t | 8字节 |
uint8_t | 无字符1字节 |
浮点数
类型 | 说明 |
---|---|
float | 单精度,4字节,32位 |
double | 双精度,8字节,64位 |
long double | 长双精度,16字节,128位 |
typedef 自定义类型
typedef uint8_t mychar;int main() { mychar ch='a'; printf("%c",ch); return 0; }
自定义类型相当于对类型起了个别名
二、goto 跳转
使用 goto 实现循环
int i=0; aa:printf("%d\n",i); i=i+1;if(i<100){ goto aa; }
标签名:
标记一个位置,goto 标签名;
跳转到指定标签位置,标签名可以自己定义。
三、输入与输出
相关函数文档参考:C语言stdio.h文档
1、字符输出
putchar:写字符到标准输出,相当于调用 putc
遍历字符并打印
#include <stdio.h>int main() { char c; for (c='a';c<='z';c++) { putchar(c); putchar('\n'); } return 0; }
说明:char 类型占用1字节,每个字符都映射对应一个整数。
查看字符对应整数
#include <stdio.h>int main() { char c; printf("=====小写====\n"); for (c='a';c<='z';c++) { printf("%d\n",c); } printf("=====大写====\n"); for (c='A';c<='Z';c++) { printf("%d\n",c); } return 0; }
大写字符和小写字符刚好差距32,
putchar('A'+32);
可转为小写。
2、字符串输出
puts:写字符串到标准输出
输出字符串示例
#include <stdio.h>int main() { char string [] = "Hello world!"; puts(string); return 0; }
3、格式化输出
printf:打印格式化数据到标准输出,如果包含格式说明符(以%开头的子串)将格式化,用附加参数替换说明符(说明符可以认为是占位符)。
格式化时用到的说明符
符号 | 解释 | 举例 |
---|---|---|
d 或者 i | 有符号十进制整数 | 392 |
u | 无符号十进制整数 | 7235 |
o | 无符号八进制 | 610 |
x | 无符号十六进制整数 | 7fa |
X | 无符号十六进制整数(大写) | 7FA |
f | 十进制浮点,小写 | 392.65 |
F | 十进制浮点,大写 | 392.65 |
e | 科学记数法(尾数/指数),小写 | 3.9265e+2 |
c | 字符 | a |
s | 字符串 | hello |
p | 指针地址(内存地址) | b8000000 |
% | %%打印出一个% | % |
格式化输出示例
#include <stdio.h>int main() { printf("字符:%c %c\n",'a',65); printf("整数:%d %ld\n",600,6500000L);//%ld 长整 printf("浮点数:%f\n",33.3); printf("十六进制:%x\n",0xffa); printf("特殊打印:%%\n"); return 0; }
结果
字符:a A整数:600 6500000 浮点数:33.300000十六进制:ffa特殊打印:%
4、输入字符和字符串
getchar:从标准输入中获取字符
获取字符示例
#include <stdio.h>int main() { uint8_t c; puts("输入文本,在一个句子中包含.以结束:"); do{ c=getchar(); putchar(c); }while (c!='.'); return 0; }
uint8_t c;
uint8_t 大小与char一样
gets:从标准输入中获取字符串
获取字符串示例
#include <stdio.h>int main() { char str[256]; puts("输入你的名字:"); gets(str); printf("你的名字:%s\n",str); return 0; }
5、格式化输入
scanf:从标准输入中读取格式化数据,格式化说明符可参照 printf
格式化输入示例
#include <stdio.h>int main() { char ch; int num; char str[10]; puts("输入一个字符:"); scanf("%c",&ch); printf("用户的输入字符为:%c\n",ch); puts("输入一个整数:"); scanf("%d",&num); printf("用户输入的整数为:%d\n",num); puts("输入字符串:"); scanf("%s",str); puts(str); return 0; }
int、char 等类型需要用&符号取得变量内存地址,变量 str 是一个数组,本身存储的就是内存地址,所以不用加上&符号。(可以理解为值与引用)
四、数组
1、一维数组
创建数组方式1:
const int NUMS_LEN=10;int nums[NUMS_LEN];
数组中元素的内存地址没有做过任何处理,有可能被之前其他程序使用过,所以数组中的默认的值是不可预期的。
演示数组中元素默认值:
#include <stdio.h>int main() { const int NUMS_LEN=10; int nums[NUMS_LEN]; for (int i = 0; i < NUMS_LEN; ++i) { printf("索引:%d,值:%d\n",i,nums[i]); } return 0; }
创建数组方式2:
int ages[]={19,20,30};
2、二维数组
创建方式1:
int nums[3][4];for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { printf("%d,%d,value=%d\n",i,j,nums[i][j]); } }
创建方式2:
int nums[2][3]={ {1,2,3}, {4,5,6} };
二维数组直接初始化值需要指定长度!
如果想对数组中的元素清0,可以在遍历中为元素赋值为0.
3、字符数组(字符串)
声明字符串并检查长度:
char str[10];printf("长度:%ld\n",strlen(str));
strlen 函数用于检测字符数组中字符的长度
存入字符串并检查长度:
char str[10]="hello";printf("长度:%ld\n",strlen(str));
\0作为字符串结尾:
char str[10]="he\0llo";printf("长度:%ld\n",strlen(str));
字符串中不论内容多少,只要遇到
\0
就认为字符串结束!
char数组填充函数 memset:
memset(要填充的数组,要填充的值,要填充多长)
#include <stdio.h>#include <string.h>int main() { char str[]="你好"; memset(str,1,6); for (int i = 0; i < 6; ++i) { printf("%d\n",str[i]); } return 0; }
关于中文:
char str[2]="你好";printf("长度:%ld\n",strlen(str));//一个汉字占用3字节
五、字符串操作
1、字符串连接
strcat:连接字符串,将源字符串的副本附加到目标字符串。
#include <stdio.h>#include <string.h>int main() { char * str="hello";//要拼接的字符 char result[100];//目标字符 memset(result,0,100);//填充0到目标字符 strcat(result,str);//拼接str到result strcat(result,"world");//拼接 world 到result puts(result); return 0; }
strncat:连接指定数量的字符到目标字符串
#include <stdio.h>#include <string.h>int main() { char dist[10]; strncat(dist,"hello",3); puts(dist); return 0; }
结果
hel
2、格式化字符串
sprintf:将格式化数据写入字符串(格式方式和其他格式化方法很像)
#include <stdio.h>#include <string.h>int main() { char str[10]; memset(str,0,10); int len,a=10,b=5; len=sprintf(str,"%d+%d=%d",a,b,a+b); printf("格式结果:[%s],长度为:%d",str,len); return 0; }
结果:
格式结果:[10+5=15],长度为:7
3、字符串与基础数据类型转换
sscanf:从字符串中读取格式化数据
使用 sscanf 截取数据
#include <stdio.h>#include <string.h>int main() { //原字符串 char sentence[]="Java is 23 years old"; //保存拆分出来的数据的变量 int age; char name[10]; sscanf(sentence,"%s %*s %d",name,&age); printf("age is %d,name is %s",age,name); return 0; }
"%s %*s %d"
分别表示了Java is 23
(空格也包含在内),%*s
表示匹配的字符串但忽略该数据(可以理解占了个位置)。&age
这里需要将基本类型的内存地址引入。
使用 sscanf 转换数据类型
#include <stdio.h>int main() { char * str="100"; int a; sscanf(str,"%d",&a); printf("转换后:%d",a); return 0; }
使用 atof 将字符串转为 double
#include <stdio.h>#include <stdlib.h>int main() { double result=atof("3.14"); printf("转换后:%f",result); return 0; }
4、字符串比较
==
用来比较两个变量的内存地址,不能比较字符串的值。使用 strcmp 比较两个字符串的值,两个字符串的值相等返回 0。
#include <stdio.h>#include <string.h>int main() { char * str1="hello"; char str2[]="hello"; if(strcmp(str1,str2)==0){ puts("两个字符串的值相等"); }else{ puts("两个字符串的值不相等"); } return 0; }
5、字符串的截取
strchr:从指定字符开始截取
#include <stdio.h>#include <string.h>int main() { char * str="helloworld"; char * result=strchr(str,'w'); puts(result); return 0; }
strrchr:从最后一个指定字符开始截取
#include <stdio.h>#include <string.h>int main() { char * str="helloworld"; char * result=strrchr(str,'l'); puts(result); return 0; }
strstr:从指定字符串开始截取
#include <stdio.h>#include <string.h>int main() { char * str="helloworld"; char * result=strstr(str,"wo"); puts(result); return 0; }
区分大小写
strncpy:从字符串中复制指定长度字符
#include <stdio.h>#include <string.h>int main() { char * str="helloworld"; char dest[10]; strncpy(dest,str,5); puts(dest); return 0; }
使用指针从指定位置开始截取
#include <stdio.h>#include <string.h>int main() { char * str="helloworld"; char * str1=str+5; puts(str1); return 0; }
可以先利用指针操作从某个位置开始截取,然后利用 strncpy 截取指定个。
六、函数
声明和调用的方式和java很相似
1、main 函数的参数
main 函数的参数,也是程序运行时的参数
声明时需指定:1.参数的长度,2.参数数组
#include <stdio.h>int main(int argc,char ** argv) { printf("参数的个数:%d\n",argc); for (int i = 0; i < argc; ++i) { printf("参数 %d 的值:%s\n",i,argv[i]); } return 0; }
2、可变参数
#include <stdio.h>#include <stdarg.h>void testarg(int n,...){ printf("参数的个数:%d\n",n); va_list args; va_start(args,n); for (int i = 0; i < n; ++i) { int temp=va_arg(args,int); printf("参数:%d\n",temp); } va_end(args); }int main(int argc,char ** argv) { testarg(2,10,55); return 0; }
函数设置可变参数时需指定参数个数形参
int n
,和...
(任意个数的参数)。需引入头文件 stdarg.h,va_list
类型保存有关变量参数的信息,va_start
初始化变量参数列表,va_arg
取出参数的值,va_end
结束使用变量参数列表
七、预处理
1、预设常量
除了可以用 #define 定义一个常量,还可以在编译参数中指定预设常量的值
编写没有声明常量但使用了常量的代码
#include <stdio.h>int main() { printf("The num is %d\n",THE_NUM); return 0; }
使用命令进行编译和运行
gcc -o test main.c -DTHE_NUM=4 test.exe
-o
参数指定编译后可执行文件的文件名,-D
参数后面直接跟常量名和值(没有空格)
2、编译条件针对不同平台编译
1.编写代码
#include <stdio.h>#define WIN 1#define LINUX 2#define MAC 3int main() {#if PLATFORM==WIN printf("Hello Windows\n");#elif PLATFORM==LINUX printf("Hello Linux\n");#elif PLATFORM==MAC printf("Hello MAC\n");#else printf("Unknow platform\n");#endif return 0; }
if、#elif、#else、#endif 这些都是编译条件,用于决定代码块是否编译
2.编译时指定条件
//编译源码gcc -o test main.c -DPLATFORM=1//运行test.exe
3、头文件重复引入
1、演示重复引入
1.创建a.h
#include "b.h"
2.创建b.h
#include "a.h"
3.引入a.h
#include <stdio.h>#include "a.h"int main() { return 0; }
2、防止头文件重复引入
修改a.h
#ifndef A_H#define A_H#include "b.h"#endif
修改b.h
#ifndef B_H#define B_H#include "a.h"#endif
通过标记一个常量来判断是否重复引入,一旦拥有这个常量将不再引入
八、指针
声明一个变量后,系统会为这个变量分配内存空间,每个变量都有一个内存地址,相当于旅馆的房间号。
我们要获取变量中的值需要通过内存地址来找到对应的值,但是还有另一种情况是这个变量中存放的是另个一变量的内存地址。这就好像你通过房间号找到这个房间,发现在这个房间里面是另一个房间的房间号。
所以,一个变量的地址就成为该变量的指针。
声明指针并指向一个变量的地址
#include <stdio.h>int main() { int *a,*b; int i=10; a=&i; b=&i; printf("a的值为:%d\n",*a); printf("b的值为:%d\n",*b); i=99; printf("a的值为:%d\n",*a); printf("b的值为:%d\n",*b); return 0; }
C语言用*表示指针,为指针赋值时
&
取出变量的地址。(指针a和指针b指向同一个地址,修改i后指针中的也会改变,这个很类似Java和C#中的引用类型)
指针的大小
#include <stdio.h>int main() { int *a; printf("size is %ld\n", sizeof(a)); return 0; }
64位系统中为8字节,32位中为4字节
1、函数指针
有参无返回值的函数指针
#include <stdio.h>void hello(int a,char* c){ printf("hello\n"); }int main() { void(*fp)(int,char*)=&hello; fp(1,""); return 0; }
有参有返回值的函数指针
#include <stdio.h>int test(int a){ printf("hello:%d\n",a); return a; }int main() { int(*fp1)(int)=&test; int result=fp1(11); printf("返回值:%d\n",result); return 0; }
给函数指针设定类型别名
#include <stdio.h>void hello(){ printf("Hello\n"); }typedef void(*SimpleHello)();int main() { SimpleHello h1=&hello; h1(); SimpleHello h2=&hello; h2(); return 0; }
重复定义时可以简化代码
2、无类型指针
无类型指针可以代表所有类型的数据
无类型指针存放字符串
#include <stdio.h>int main() { void *test="Hello"; printf("%s\n",test); return 0; }
九、结构体与共同体
1、结构体
主要用结构体描述属性
定义使用结构体
#include <stdio.h>struct File{ char* name; int size; };int main() { struct File file; file.name="a.txt"; file.size=10; printf("文件 %s 的大小为 %d\n",file.name,file.size); struct File file1={"a.jpg",100}; printf("文件 %s 的大小为 %d\n",file1.name,file1.size); return 0; }
结构体也可以像数组那样初始化(C#中也有结构,两者比较像可以对比记忆)
定义结构体类型简化代码
#include <stdio.h>typedef struct _File{ char* name; int size; }File;int main() { File file; file.name="a.txt"; file.size=10; printf("文件 %s 的大小为 %d\n",file.name,file.size); File file1={"a.jpg",100}; printf("文件 %s 的大小为 %d\n",file1.name,file1.size); return 0; }
2、结构体的内存对齐
结构体所占大小如何计算的?结构的大小由各变量与字节最大变量对齐后相加得出。
结构体内存对齐
如图所示,三种情况:
1.当定义第一个变量大小为1字节,第二个变量比第一个变量大时,第一个变量要对齐大的变量,第一个变量填充为2字节,结构体大小为4字节。
2.前两变量加起来不足对齐第三个变量的大小,所以填充2字节对齐,结构体大小为8字节。
3.前4个变量加起来刚好对齐第5个变量的大小,无须对齐。最后一个变量没有对齐则填充2个字节对齐第5个变量,所以结构体大小为12字节。
#include <stdio.h>typedef struct _Data{ uint8_t a;//1 uint8_t b;//1 uint8_t c;//1 uint8_t d;//1 uint32_t e;//4 uint8_t f;//1}Data;int main() { printf("size is %d\n", sizeof(Data)); return 0; }
此时结果为12
3、结构体指针
没有使用指针前
#include <stdio.h>typedef struct _Dog{ char * color; int age; }Dog;int main() { Dog dog1={"红色",2}; Dog dog2=dog1; dog1.age=3; printf("(dog1 color is %s,age is %d),(dog2 color is %s,age is %d)\n" ,dog1.color,dog1.age,dog2.color,dog2.age ); return 0; }
没有使用之前将dog1赋值给dog2相当于复制一个副本
使用结构体指针
#include <stdio.h>typedef struct _Dog { char *color; int age; } Dog;int main() { Dog dog1 = {"红色", 2}; Dog *dog2 = &dog1; dog1.age = 3; printf("(dog1 color is %s,age is %d),(dog2 color is %s,age is %d)\n", dog1.color, dog1.age, dog2->color, dog2->age); return 0; }
指针操作用法和之前一样,需要注意的地方是:结构体指针,获取结构体的值时需要使用
->
,否则将编译错误
通过函数创建和销毁结构体指针
#include <stdio.h>#include <stdlib.h>typedef struct _Dog { char *color; int age; } Dog;Dog* createDog(char* color,int age){ Dog* dog=malloc(sizeof(Dog)); dog->color=color; dog->age=age; return dog; }void deleteDog(Dog* dog){ free(dog); }int main() { Dog* dog1=createDog("红色",3); printf("(dog1 color is %s,age is %d)\n", dog1->color, dog1->age); deleteDog(dog1); return 0; }
malloc函数分配内存,使用完毕后用free函数释放内存(释放后的内存的值不可预期何时修改,free函数调用后别的程序就可以使用这块空间了)
3、共同体
共同体的特点:共同体中声明的所有变量共用一块空间
声明及使用共同体
#include <stdio.h>typedef union _BaseData{ char ch; uint8_t ch_num; }BaseData;int main() { BaseData data; data.ch='A'; printf("ch_num is %d size is %d\n",data.ch_num, sizeof(BaseData)); return 0; }
变量 ch_num 和 ch 共用同一内存地址,大小为 1,共同体做类型转换比较方便。
实现颜色的操作
#include <stdio.h>//定义颜色结构体typedef struct _ColorARGB{ uint8_t blue; uint8_t green; uint8_t red; uint8_t alpha; }ColorARGB;//定义颜色共同体typedef union _Color{ uint32_t color; ColorARGB colorARGB; }Color;int main() { Color c; c.color=0xFFAADD00; printf("red:%X",c.colorARGB.red); return 0; }
存入的数据是以小端模式,所以定义颜色结构体时的通道的顺序和赋值的颜色顺序相反。结合结构体与共同体实现(巧妙)
十、文件操作
fopen函数:以指定文件名和模式获取一个流的FILE指针。
支持的模式
模式 | 说明 |
---|---|
"r" | 读:文件必须存在 |
"w" | 写:创建一个文件,如果已经存在则覆盖 |
"a" | 追加:向文件末尾追加内容,如果文件不存在则创建该文件 |
"r+" | 可做读取和修改的操作,文件必须存在 |
写文件
//创建文件指针FILE * file=fopen("a.txt","w");if (file){ //写入内容 fputs("HelloWorld",file); //关闭 fclose(file); }else{ puts("文件操作失败!"); }
读文件
#include <stdio.h>int main() { FILE * file=fopen("a.txt","r"); if (file){ char ch; while(1){ ch=fgetc(file); if(ch!=EOF){ printf("%c\n",ch); }else{ break; } } fclose(file); } return 0; }
作者:享智同行
链接:https://www.jianshu.com/p/63234193e729
共同学习,写下你的评论
评论加载中...
作者其他优质文章