目录
一、基础语法
1.标识符
C 标识符是用来标识变量、函数,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9);C 语言是区分大小写的编程语言。
2.关键字
下表列出了 C 中的保留字。这些保留字不能作为常量名、变量名或其他标识符名称。
基本数据类型(5个)
-
void:声明函数无返回值或无参数
-
char:字符型类型数据
-
int:整型数据
-
float:单精度浮点型数据
-
double:双精度浮点型数据
修饰关键字(4个)
-
short:修饰int,短整型数据,可省略被修饰的int。
-
long:修饰int,长整形数据,可省略被修饰的int。
-
signed:修饰整型数据,有符号数据类型
-
unsigned:修饰整型数据,无符号数据类型
复杂类型关键字(5个)
-
struct:结构体声明
-
union:共用体声明
-
enum:枚举声明
-
typedef:声明类型别名
-
sizeof:得到特定类型或特定类型变量的大小
存储级别关键字(6个)//记住前三个(static,const,volatile)
-
volatile:与const合称“cv特性”,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值
-
static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部
-
const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)
-
extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明的对象的一个“引用“
- register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数
- auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配
跳转结构(4个)
-
return:用在函数体中,返回特定值(或者是void值,即不返回值)
-
continue:结束当前循环,开始下一轮循环
-
break:跳出当前循环或switch结构
-
goto:无条件跳转语句
分支结构(5个)
-
if:条件语句
-
else:条件语句否定分支(与if连用)
-
switch:开关语句(多重分支语句)
-
case:开关语句(switch)中的分支标记
-
default:开关语句中的“其他”分治,可选。
循环结构(3个)//当循环条件表达式为真则继续循环,为假则跳出循环。
-
for:for循环结构,for(1;2;3)4;的执行顺序为1->2->4->3->2...循环,其中2为循环条件
-
do:do循环结构,do 1 while(2);的执行顺序是1->2->1...循环,2为循环条件
-
while:while循环结构,while(1) 2;的执行顺序是1->2->1...循环,1为循环条件
3.分号
在 C 程序中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束;语句{}后不用写;初始化赋值语句后需要;。
4.注释
以 // 开始的单行注释,这种注释可以单独占一行。
/* */ 这种格式的注释可以单行或多行。
不能在注释内嵌套注释,注释也不能出现在字符串或字符值中。
5.空格
只包含空格的行,被称为空白行,可能带有注释,C 编译器会完全忽略它。
在 C 中,空格用于描述空白符、制表符、换行符和注释。空格分隔语句的各个部分,让编译器能识别语句中的某个元素(比如 int)在哪里结束,下一个元素在哪里开始。
6.main(一般认为主程序)
程序执行将从main函数开始,也将从main函数结束,对于他人程序的理解需要从main函数开始阅读。
二、输入输出
键盘->内存缓冲区->程序输入(stdin)是C语言中标准输入流,一般用于获取键盘输入到缓冲区里的东西。
1. scanf()输入
1.1从键盘取值是以格式控制符为准,从buf(缓冲区)取符合的字符数据。不匹配停止取数据。在读取字符串时,只要遇到空格,就会停止读取。
scanf("%s %d",a,&b);//a字符串,b字符
1.2 ret可以记录scanf读取次数
int data,dete;
int ret = scanf("%d%d",&data,&dete);
1.3 连续输入时,scanf俩格式控制符之间有什么,键入时就加上什么
int a,b,c,d; //会忽略任何空白字符(空格、回车、制表符等)
scanf("%d %d",&a,&b); //数与数间加空格
scanf("%d,%d",&c,&d); //数与数间加逗号
scanf("%d%d",&c,&d); //数与数间加空格
1.3.1 scanf()一个格式控制符我们却输入了两次,这是因为我们在scanf()里加入了’ \n ‘。
scanf("%d\n%d",&a,&b);
总结:scanf可以根据不同格式符,在缓冲区拿取不同数据,遇到空格结束,回车确认,对这个回车确认符并不进行处理,回车符会留在输入缓存区中。因此,在下一个读“字符”操作函数(getchar, scanf("%c"), gets()等)运行时,会读到这个字符(%d会忽略任何空白字符(空格、回车、制表符等)忽略是从缓冲区里删除,但并不保存)(%s一直找到\0结束,否则有可能出现段错误或乱码)。
2. printf()输出
int printf(const char *format, ...) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
- %s:将字符串输出。
- %c:将字符输出。
- %d:将十进制整数以带符号方式输出。
- %ld:将长整型以带符号方式输出。
- %f:将浮点数以小数形式输出。
- %e:将浮点数以指数形式输出。
- %x:将十六进制整数以无符号方式输出,并且以小写字母表示a-f。
- %X:将十六进制整数以无符号方式输出,并且以大写字母表示A-F。
- %u:将十进制整数以无符号方式输出。
- %p:输出指针地址值。
- %o:将八进制形式输出一个整数。
输出不仅要注意格式控制符所影响的内容输出,还要注意输入数据格式时对数据的影响。
3. getchar()
int getchar(void),所以它返回的是ASCII码,所以只要是ASCII码表里有的字符它都能读取出来。在调用getchar()函数时,编译器会依次读取用户键入缓存区的一个字符(注意这里只读取一个字符,如果缓存区有多个字符,那么将会读取上一次被读取字符的下一个字符),如果缓存区没有用户键入的字符,那么编译器会等待用户键入并回车后再执行下一步 (注意键入后的回车键也算一个字符,输出时直接换行)。
4. putchar()
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。可以在循环内使用这个方法
int c;
c = getchar( );
putchar( c );
5. gets() & puts() 函数
char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout。
三、 运算符
1. 运算符优先级
2. 惰性运算
逻辑与 :错&&?(右不执行)
逻辑或 :真||? (右不执行)
3.位与、位或、异或
位或运算 | :有1为1,全0为0
位与运算 & :有0为0,全1为1
异或运算 ^ :异则为1,同则为0
4.位移
数左移<<n : 数*2^n
数右移>>n : 数/(2^n)
5.强制转换
隐式转换:系统按照隐式规则自动进行的转换
强制转换:用户显式自定义进行的转换(不存在四舍五入)
隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度
6.逗号表达式
(表达式1,表达式2,表达式3表达式n)求值顺序:
先求表达式1,再求表达式2,再求表达式3,最后求表达式n整个逗号表达式的值为表达式n的值
逗号表达式的优先级最低,运算顺序是从左往右,整个表达式的值为表达式最右边的值
int a = 10, 11, 12, 13; //a = 10
int b = (10, 11, 12, 13) //b = 13根据优先级,该式为赋值表达式
注意:赋值表达式:f =(1,2)//括号优先级高,先处理括号里的逗号表达式,再赋值
逗号表达式:a<1,b>2 a=1,b=2 f=1.0,2.0
7.其他
无符号数加上有符号数
先将有符号数转为无符号数(负数的补码当做正数二进制),然后进行无符号数计算
unsigned int a = 6; //0000 0000 0000 0110正数原反补一致
int b = -20; //1000 0000 0001 0100-补->1111 1111 1110 1100
(a+b>b)?printf(">6"):printf("<6"); //1111 1111 1111 0010>6,三目运算符输出“>6”
除号/ :得数为整形
==和=不能在一个表达式里,编译出错。
前后缀运算符(++,--)不能用于常量和表达式,例如5++,--(a+b)。
取模运算符左右都为整数。
四、常用函数集合
1.sizeof
sizeof() 是一个运算符(单目)而不是函数,用于计算一个类型或变量所占用的内存字节数。可以用它来获取任何类型的数据的字节数,包括基本数据类型、数组、结构体、共用体等等。
指针大小取决于系统位数
int *p = NULL;
sizeof(p);//在32位系统,指针的大小占用4字节
sizeof(p);//在64位系统,指针的大小占用8字节
2. strlen
只能用来求字符串长度的函数,遇到‘\0’停止,且长度不包含‘\0’,头文件<string.h>。
注意:strlen() 函数只能用于计算以空字符 '\0' 结尾的字符串的长度,如果字符串中没有空字符 '\0' ,则 strlen() 函数的行为是未定义的。
sizeof和strlen的区别:
-
sizeof()
是一个运算符,而strlen()
是一个函数。 -
sizeof()
计算的是变量或类型所占用的内存字节数(空间大小),而strlen()
计算的是字符串中字符的个数。 -
sizeof()
可以用于任何类型的数据,而strlen()
只能用于以空字符 '\0' 结尾的字符串。 -
sizeof() 计算字符串的长度,包含末尾的 '\0',strlen() 计算字符串的长度,不包含字符串末尾的 '\0'
char arr[20]= {"jhsgdjaggf"};
int a = sizeof(arr);//20
int b = strlen(arr);//10
printf("%d\n%d",a,b);
3. strstr
4. strtok
注意:
-
该函数会将改变原始字符串 str,使其所包含的所有分隔符变成结束标记 ‘\0’ 。
-
由于该函数需要更改字符串 str,因此 str 指向的内存必须是可写的。
-
首次调用时 str 指向原始字符串,此后每次调用 str 用 NULL 代替。
char s[20] = "yueqian\ncom\ncn";
char *p = strtok(s, "\n"); // 首次调用时,s 指向需要分割的字符串
while(p != NULL)
{
printf("%s\n", p);
p = strtok(NULL, "."); // 此后每次调用,均使用 NULL 代替。
}
5. strcat和strncat
注意:
-
这两个函数的功能,都是将 src 中的字符串,复制拼接到 dest 的末尾。
-
strcat() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。
-
strncat() 有边界控制,最多复制 n+1 个字符(其中最后一个是 ‘\0’ )到 dest 的末尾
char s1[10] = "abc";
strcat(s1, "xyz");
printf("%s\n", s1); // 输出 "abcxyz"
char s2[10] = "abc";
strcat(s3, "123456789"); // 此处操作内存溢出,可能会发生内存崩溃
char s[10] = "abc";
strncat(s, "123456789", sizeof(s)-strlen(s)-1);
printf("%s\n", s); // 输出 "abc123456",两个字符串被拼接到了一起,且不会溢出
6. strcpy和strncpy
注意:
-
这两个函数的功能,都是将 src 中的字符串,复制到 dest 中。
-
strcpy() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。
-
strncpy() 有边界控制,最多复制 n+1 个字符(其中最后一个是 ‘\0’ )到 dest 中 。
char s1[5] = "abc";
strcpy(s1, "xyz);
printf("%s\n", s1); // 输出 "xyz",原有的"abc"被覆盖
char s2[5] = "abc";
strcpy(s2, "123456789"); // 此处操作内存溢出,可能会发生内存崩溃
char s[5] = "abc";
strncpy(s, "123456789", sizeof(s)-1);
printf("%s\n", s); // 输出 "1234",有边界保护,不会溢出
7. strcmp和strncmp(返回值只有正0负)
注意: 比较字符串大小,实际上比较的是字符的 ASCII码值的大小。 从左到右逐个比较两个字符串的每一个字符,当能“决出胜负”时立刻停止比较。
printf("%d\n", strcmp("abc", "abc")); // 输出0,两个字符串相等
printf("%d\n", strcmp("abc", "aBc")); // 输出正数,"abc" 大于 "aBc"
printf("%d\n", strcmp("999", "aaa")); // 输出负数,"999" 小于 "aaa"
8. strchr和strrchr
注意:
-
这两个函数的功能,都是在指定的字符串 s 中,试图找到字符 c。
-
strchr() 从左往右找,strrchr() 从右往左找。
-
字符串结束标记 ‘\0’ 被认为是字符串的一部分。
char *p;
p = strchr("www.qq.com", '.'); // 从左到右找到第一个出现的字符'.'
printf("%s\n", p); // 输出 ".qq.com"
p = strrchr("www.qq.com", '.');// 从右到左找到第一个出现的字符'.'
printf("%s\n", p); // 输出 ".com"
9.malloc&calloc&realloc
malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。realloc尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
#include<stdlib.h>//头文件
int *p = malloc(3*sizeof(int));//申请3块大小为int的堆空间内存
bzero(p,3*sizeof(int));//堆内存清零
free(p);//释放空间锚点
#include<stdlib.h>//头文件
int *k = calloc(3,sizeof(int));//申请3块连续的大小为int的堆空间内存
//空间自动清零
free(k);//释放空间锚点
//原址扩容空间充足会在原地址上扩容;不足则另起炉灶,会改变空间地址
#include <stdio.h>
#include <stdlib.h>//头文件
#include <string.h>
int main(int argc, char const *argv[])
{
// 申请堆空间
char *p = calloc(1,sizeof(char));
if(p == NULL)//判断功能是否正常
{
printf("calloc failed:");
return -1;
}
char buf[] = "afjoiajfajflajlfjalflafjslfjsljfls";
// 扩容,否则会溢出,扩容后会复制p到ptr
char *ptr = realloc(p,100);
strcpy(ptr,buf);
printf("%s\n",ptr);
return 0;
}
释放内存的含义: 释放内存意味着将内存的使用权归还给系统。 释放内存并不会改变指针的指向。 释放内存并不会对内存做任何修改,更不会将内存清零。
free()只能释放堆内存,并且只能释放整块堆内存,不能释放别的区段的内存或者释放一部分堆内存。
未完待续,发现错误请留言指正(万分感谢)。