1.关键字
一、数据类型关键字
A基本数据类型(5个)
- void:声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果
- char:字符型类型数据,属于整型数据的一种
- int:整型数据,通常为编译器指定的机器字长
- float:单精度浮点型数据,属于浮点数据的一种
- double:双精度浮点型数据,属于浮点数据的一种
B类型修饰关键字(4个)
- short:修饰int,短整型数据,可省略被修饰的int。
- long:修饰int,长整形数据,可省略被修饰的int。
- signed:修饰整型数据,有符号数据类型
- unsigned:修饰整型数据,无符号数据类型
C复杂类型关键字(5个)
- struct:结构体声明
- union:共用体声明
- enum:枚举声明
- typedef:声明类型别名
- sizeof:得到特定类型或特定类型变量的大小
D存储级别关键字(6个)
- auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配
- static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部
- register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数
- extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明的对象的一个“引用“
- const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)
- volatile:与const合称“cv特性”,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值
二、流程控制关键字
A跳转结构(4个)
- return:用在函数体中,返回特定值(或者是void值,即不返回值)
- continue:结束当前循环,开始下一轮循环
- break:跳出当前循环或switch结构
- goto:无条件跳转语句
B分支结构(5个)
- if:条件语句
- else:条件语句否定分支(与if连用)
- switch:开关语句(多重分支语句)
- case:开关语句中的分支标记
- default:开关语句中的“其他”分治,可选。
C循环结构(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为循环条件
以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。
关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。比如:
//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main()
{//观察num1和num2,这两个变量的类型是一样的unsigned int num1 = 0;uint_32 num2 = 0;return 0; }
关键字static
在C语言中:
static是用来修饰变量和函数的
- 修饰局部变量-称为静态局部变量
- 修饰全局变量-称为静态全局变量
- 修饰函数-称为静态函数
1 修饰局部变量
对比代码1和代码2的效果理解static修饰局部变量的意义。
//代码1
#include <stdio.h>
void test()
{int i = 0;i++;printf("%d ", i); //每次函数执行结束,i值都会被释放掉
}
int main()
{int i = 0;for(i=0; i<10; i++){test(); //每次有打印1}return 0; }
12345678910111213141516
//代码2
#include <stdio.h>
void test()
{//static修饰局部变量static int i = 0; //test()执行结束后,保留i值i++;printf("%d ", i);
}
int main()
{int i = 0;for(i=0; i<10; i++){test(); //每次有打印1到10}return 0; }
1234567891011121314151617
结论:static修饰局部变量改变了变量的生命周期,让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
2 修饰全局变量
代码1正常,代码2在编译的时候会出现连接性错误。
//代码1
//add.c
int g_val = 2018;
//test.c
int main()
{printf("%d\n", g_val);return 0; }//代码2
//add.c static 全局变量
static int g_val = 2018;
//test.c
int main()
{printf("%d\n", g_val);return 0; }
1234567891011121314151617
结论:一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
3 修饰函数
代码1正常,代码2在编译的时候会出现连接性错误,和修饰全局变量一样。
//代码1
//add.c
int Add(int x, int y) {return x+y; }
//test.c
int main()
{printf("%d\n", Add(2, 3));return 0; }//代码2
//add.c static 修饰函数
static int Add(int x, int y) {return c+y; }
//test.c
int main()
{printf("%d\n", Add(2, 3));return 0; }
12345678910111213141516171819
结论:一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
2.基础数据类型 和 尺寸
char //字符数据类型short //短整型int //整形long //长整型long //更长的整形float //单精度浮点数double //双精度浮点数//C语言有没有字符串类型? 答案是没有。c语言中没有string类型,string是用char型数组来构造的。
每种数据类型占据的字节大小:
#include <stdio.h>
int main()
{//以下是在win10的vs2017显示的结果printf("%d\n", sizeof(char)); // 1printf("%d\n", sizeof(short)); // 2printf("%d\n", sizeof(int)); // 4printf("%d\n", sizeof(long)); // 4printf("%d\n", sizeof(long long)); //8printf("%d\n", sizeof(float)); //4printf("%d\n", sizeof(double)); // 8printf("%d\n", sizeof(long double)); //8return 0; }
12345678910111213
存在这么多的类型,其实是为了更加丰富的表达生活中的各种值。
类型的使用:
char ch = 'w'; //字符型
int weight = 120; //整型
float salary = 20000.0f; //单精度浮点型
3. 变量、常量
生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等)
有些值是可变的(比如:年龄,体重,薪资)。
不变的值,C语言中用常量的概念来表示,变得值C语言中用变量来表示。
3.1 定义变量的方法
语法:变量类型 变量名称 = 初始值;
int age = 150;
float weight = 45.5f;
char ch = 'w';
123
3.2 变量的分类
1)局部变量
2)全局变量
#include <stdio.h>
int global = 2019;//全局变量
int main()
{int local = 2018;//局部变量//下面定义的global会不会有问题?没有问题int global = 2020;//局部变量,当局部变量和全局变量同名时,优先使用局部变量,这个人感觉和搜索路径相关!printf("global = %d\n", global);return 0; }
3.3 变量的使用(使用 scanf如何 接收数据)
#include <stdio.h>
int main()
{int num1 = 0;int num2 = 0;int sum = 0;printf("输入两个操作数:>");scanf("%d %d", &num1, &num2); //int -- %d float -- %f double -- %lf char -- %csum = num1 + num2;printf("sum = %d\n", sum);return 0; }
在C语言中,scanf
函数可以用来输入long
,long long
和short
类型的数据。下面是一些示例:
#include <stdio.h>int main() {long a;long long b;short c;printf("请输入一个long类型的数:");scanf("%ld", &a);printf("请输入一个long long类型的数:");scanf("%lld", &b);printf("请输入一个short类型的数:");scanf("%hd", &c);printf("你输入的long类型的数是:%ld\n", a);printf("你输入的long long类型的数是:%lld\n", b);printf("你输入的short类型的数是:%hd\n", c);return 0;
}
在这个代码中,%ld
,%lld
和%hd
分别用于输入long
,long long
和short
类型的数据。&
符号是取地址运算符,它的作用是获取变量的内存地址。scanf
函数需要这个地址来改变变量的值。注意,scanf
函数的使用需要谨慎,因为它可能会导致缓冲区溢出等问题。在实际编程中,建议使用更安全的输入函数,如fgets
和sscanf
。
3.4 变量的作用域和生命周期
1)作用域:作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用
的。而限定这个名字的可用性的代码范围就是这个名字的作用域。
(1)局部变量的作用域是变量所在的局部范围。比如:{int a = 10;},{}内就是局部变量a的作用域。
(2)全局变量的作用域是整个工程。比如:int b =100;int main(){};,在main()函数{}之外也不在其他范围内的变量。
2)生命周期:变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
(1)局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。比如函数的形参。
(2)全局变量的生命周期是:整个程序的生命周期。程序结束,全局变量生命周期才结束。
3.5 常量
C语言中的常量和变量的定义的形式有所差异。
C语言中的常量分为以下以下几种:
1)字面常量
2)const 修饰的常变量 语法: const 变量类型
3)#define 定义的标识符常量 语法:#define 常量标识符 常量值
4)枚举常量 语法:
enum 枚举常量名 {常量1, 常量2,…};
//这里记得加上分号!常量面前没有常量类型
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//举例
enum Sex
{MALE,//MALE=2, //可指定默认值,后面的值在2的基础上,递增+1FEMALE,SECRET
}; //这里要加上分号;
//括号中的MALE,FEMALE,SECRET是枚举常量int main()
{//字面常量演示3.14;//字面常量1000;//字面常量//const 修饰的常变量const float pai = 3.14f; //这里的pai是const修饰的 常变量//pai = 5.14;//是不能直接修改的!vs2017会提示表达式必须是可修改的左值//#define的标识符常量 演示#define MAX 100 //这里没有分号”;“printf("max = %d\n", MAX); // 100//枚举常量演示printf("%d\n", MALE); // 0printf("%d\n", FEMALE); // 1printf("%d\n", SECRET); // 2//注:枚举常量的默认是从0开始,依次向下递增1的return 0;
}
注(常变量):
上面例子上的 pai 被称为 const 修饰的常变量, const 修饰的常变量在C语言中只是在语法层面限制了变量 pai 不能直接被改变,但是 pai 本质上还是一个变量的,所以叫常变量。
4. 字符串+转义字符
4.1 字符串
"hello bit.\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
注:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志,不算作字符串内容。
#include <stdio.h>
//下面代码,打印结果是什么?为什么?(突出'\0'的重要性)
int main()
{char arr1[] = "bit"; char arr2[] = { 'b', 'i', 't' };char arr3[] = { 'b', 'i', 't', '\0' }; printf("%s\n", arr1); //bit 默认包含了'\0'printf("%d\n", sizeof(arr1)/sizeof(char)); //4, 但是在计算长度的时候是包括的!!!printf("%s\n", arr2); //bit烫烫烫烫蘠it 随机值//printf("%d\n", sizeof(arr2) / sizeof(char));printf("%s\n", arr3); //bit 显示写出'\0'printf("%d\n", sizeof(arr3) / sizeof(char)); // 4 return 0;
}
4.2 转义字符
加入我们要在屏幕上打印一个目录: c:\code\test.c
我们该如何写代码?
#include <stdio.h>
int main()
{printf("c:\code\test.c\n");return 0; }
12345
实际上程序运行的结果是这样的:
这里就不得不提一下转义字符了。转义字符顾名思义就是转变意思。
下面看一些转义字符。
#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>
int main()
{//问题1:在屏幕上打印一个单引号',怎么做?//问题2:在屏幕上打印一个字符串,字符串的内容是一个双引号“,怎么做?printf("%c\n", '\''); //注意:单引号''括起来的对应%c,双引号括起来的对应%sprintf("%s\n", "\'"); printf("%s\n", "\"");printf("%c\n", '\"');return 0;
}
12345678910111213
//程序输出什么?
#include <stdio.h>
int main()
{//strlen()求字符串长度printf("%d\n", strlen("abcdef")); // 6// \62被解析成一个转义字符printf("%d\n", strlen("c:\test\628\test.c")); //14 = 11 + 3个转义字符printf("%c\n", '\62'); //2 ???return 0;
}
4.3字符指针和字符数组 和字符串的区别
在C语言中,字符指针、字符数组和字符串是三个不同的概念,虽然它们在许多情况下可以互换使用,但它们之间还是存在一些关键的区别123456。
字符指针是一个指针,它存储的是地址,而不是将字符串放到字符指针变量中123456。例如:
char *p = "Hello World!";
在这个例子中,p
是一个字符指针,它指向的是字符串"Hello World!"的首地址123456。
字符数组是一个存储字符的数组,其长度是固定的,其中任何一个数组元素都可以为 null 字符。因此,字符数组不一定是字符串123456。例如:
char cArr[] = {'H', 'e', 'l', 'l', 'o'};
在这个例子中,cArr
是一个字符数组,它包含5个字符,但并没有以null字符(‘\0’)结束,所以它不是一个字符串123456。
字符串,它必须以 null 字符结束,其后的字符不属于该字符串123456。字符串一定是字符数组,它是最后一个字符为 null 字符的字符数组123456。例如:
char sArr[] = "Hello";
在这个例子中,sArr
是一个字符串,它包含5个字符和一个结束的null字符(‘\0’),所以它是一个字符串123456。
需要注意的是,字符串是一个只读型字符型数组,不能够通过指针更改字符串内部数据2。而字符数组可以修改其内部的数据123456。
总的来说,所有的字符串都是字符数组,但并非所有的字符数组都是字符串123456。这是因为字符串有一个额外的约束,即必须以null字符结束123456。这个约束使得字符串可以被一些特定的函数(如printf
和strlen
)用于特定的目的123456。
进阶
1 字符函数和字符串函数
**1.1 **strlen (重要)
函数用于获取字符串的长度:
size_t strlen ( const char * str );
1
字符串已 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(*不包含 ‘\0’* )。
参数指向的字符串必须要以 ‘\0’ 结束。,没有’\0’,的出来的结果就是随机值
注意函数的返回值为size_t,是无符号的( 易错 )
学会strlen函数的模拟实现
#include <stdio.h>int main()
{const char*str1 = "abcdef";const char*str2 = "bbb";if(strlen(str2)-strlen(str1)>0) // 返回无符号的,就一直是>0{printf("str2>str1\n");} else{printf("srt1>str2\n");}return 0;
}
这段代码中的问题在于 strlen
函数返回的是 size_t
类型,这是一个无符号整数类型。当你从一个较大的无符号数中减去一个较小的无符号数时,结果仍然是一个无符号数。在你的例子中,strlen(str2)
是3,strlen(str1)
是6,所以 strlen(str2) - strlen(str1)
的结果是一个非常大的无符号数,而不是一个负数。这就是为什么 if(strlen(str2)-strlen(str1)>0)
总是为真的原因。
为了避免这个问题,你可以先将 strlen
的结果存储在 int
类型的变量中,然后再进行比较,就像你在第二段代码中所做的那样。这样,b - a
的结果就会是一个有符号的整数,可以正确地表示负数。这就是为什么第二段代码能够正确地比较两个字符串的长度。希望这个解释对你有所帮助!
#include <stdio.h>int main()
{const char*str1 = "abcdef";const char*str2 = "bbb";printf("%d\n", strlen(str1)); // 6printf("%d\n", strlen(str2)); // 3int a = strlen(str1);int b = strlen(str2);int c = b - a;if (c > 0){printf("str2>str1\n");}else{printf("srt1>str2\n");}return 0;
}
1.2 strcpy
函数用于复制字符串:
char * strcpy ( char * destination, const char * source );
1
Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
#include <stdio.h>
#include <string.h>int main() {char src[40];char dest[12];memset(dest, '\0', sizeof(dest));strcpy(src, "This is tutorialspoint.com");strcpy(dest, src);printf("Final copied string : %s\n", dest);return(0);
}
Final copied string : This is tutorialspoint.com
1.3 strcat
strcat
是C语言中的一个库函数,用于将一个字符串追加到另一个字符串的末尾12345。其函数原型为:
char *strcat (char *dest, const char *src);
其中,dest
是目标字符串,src
是要追加的字符串4。
这个函数的工作原理是将src
所指向的字符串追加到dest
所指向的字符串的结尾13。因此,必须确保dest
有足够的内存空间来容纳两个字符串,否则可能会导致溢出错误3。
以下是strcat
函数的一个使用示例1:
#include <stdio.h>
#include <string.h>int main () {char src[50], dest[50];strcpy(src, "This is source");strcpy(dest, "This is destination");strcat(dest, src);printf("Final destination string: |%s|", dest);return(0);
}
在这个示例中,src
字符串被追加到dest
字符串的末尾,然后打印出最终的dest
字符串。这将产生以下结果:
Final destination string: |This is destinationThis is source|
需要注意的是,strcat
函数不会覆盖目标字符串1。而且,src
和dest
字符串中都必须包含字符’\0’,并且src
字符串必须以’\0’结尾,否则追加过程无法顺利实现2。此外,目标字符串空间必须足够大(足够容纳追加字符串src
的内容),并且目标空间必须可修改(前面不能加const并且不能说常量字符串)2。
字符串自己给自己追加,如何?会报错
/* strcat example */
#include <stdio.h>
#include <string.h>int main ()
{char str[80];strcpy(str,"these ");strcat(str,"strings ");strcat(str,"are ");strcat(str,"concatenated.");puts (str);return 0;
}
these strings are concatenated.
1.4 strcmp
strcmp
函数可以用来比较两个 C 字符串。在 C 语言中,字符串通常表示为字符数组,并以空字符 ‘\0’ 结尾。所以,如果你有两个字符数组,并且它们都以 ‘\0’ 结尾,那么你可以使用 strcmp
来比较它们。
但是,需要注意的是,strcmp
函数并不会比较两个数组的所有元素,它只会比较到遇到第一个 ‘\0’ 字符为止。所以,如果你的数组中包含 ‘\0’ 字符,或者你想比较的是非字符类型的数组,那么 strcmp
可能就不适用了。
在这种情况下,你可能需要使用其他函数,如 memcmp
,它可以比较任意类型的数组,只需要指定要比较的字节数。希望这个解释对你有所帮助!
比较字符数组
当然可以。以下是一个使用 strcmp
来比较两个字符数组的例子:
#include <stdio.h>
#include <string.h>int main() {char str1[] = "Hello, world!";char str2[] = "Hello, world!";if (strcmp(str1, str2) == 0) {printf("The two strings are equal.\n");} else {printf("The two strings are not equal.\n");}return 0;
}
在这个例子中,我们定义了两个字符数组 str1
和 str2
,然后使用 strcmp
来比较这两个数组是否相等。如果 strcmp
返回 0,那么这两个字符串就是相等的。否则,它们就不相等。希望这个例子对你有所帮助!
The two strings are equal.
比较字符串
函数用于比较两个字符串:
int strcmp ( const char * str1, const char * str2 );
1
This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串?
#include <stdio.h>
int main()
{const char*str1 = "abcdef";const char*str2 = "abcfed";printf("%d\n", strlen(str1)); // 6printf("%d\n", strlen(str2)); // 3int a = strcmp(str2, str1);printf("%d\n", a);return 0;
}
6
6
1
#include <stdio.h>
#include <string.h>int main() {char str1[15];char str2[15];int ret;strcpy(str1, "abcdef");strcpy(str2, "ABCDEF");ret = strcmp(str1, str2);if(ret < 0) {printf("str1 is less than str2\n");} else if(ret > 0) {printf("str2 is less than str1\n");} else {printf("str1 is equal to str2\n");}return(0);
}
str2 is less than str1
1.5 strncpy
函数用于复制指定数量的字符:
strncpy
是 C 语言中的一个内置函数,用于将一个字符串的前 n 个字符复制到另一个字符串1。它的声明如下:
char *strncpy(char *dest, const char *src, size_t n);
这里的参数是:
dest
:我们想要复制到的目标字符串1。src
:我们要复制的源字符串1。n
:要复制的字符的最大数量1。
strncpy
函数的返回值是指向结果字符串 dest
的指针1。
如果 src
的长度小于 n
,那么 dest
的剩余部分将用空字符 ‘\0’ 填充1。
以下是一个 strncpy
函数的使用示例:
#include <stdio.h>
#include <string.h>int main() {char src[40];char dest[12];memset(dest, '\0', sizeof(dest));strcpy(src, "This is tutorialspoint.com");strncpy(dest, src, 10);printf("Final copied string : %s\n", dest);return 0;
}
在这个例子中,我们将 src
字符串的前 10 个字符复制到 dest
字符串1。
Final copied string : This is tu
/* strncpy example */
#include <stdio.h>
#include <string.h>int main ()
{char str1[]= "To be or not to be";char str2[40];char str3[40];/* copy to sized buffer (overflow safe): */strncpy( str2, str1, sizeof(str2) );/* partial copy (only 5 chars): */strncpy( str3, str2, 5 );str3[5] = '\0'; /* null character manually added */puts (str1);puts (str2);puts (str3);return 0;
}
To be or not to be
To be or not to be
To be
1.6 strncat (连接)
strncat
是 C 语言中的一个内置函数,用于将一个字符串的前 n 个字符追加到另一个字符串的末尾1。它的声明如下:
char *strncat(char *dest, const char *src, size_t n);
这里的参数是:
dest
:我们想要追加的字符串1。src
:我们要追加的 ‘n’ 个字符的字符串1。n
:要追加的字符的最大数量1。
strncat
函数的返回值是指向结果字符串 dest
的指针1。
以下是一个 strncat
函数的使用示例:
#include <stdio.h>
#include <string.h>int main() {char src[50], dest[50];strcpy(src, "This is source");strcpy(dest, "This is destination");strncat(dest, src, 15);printf("Final destination string : |%s|\n", dest);return 0;
}
在这个例子中,我们将 src
字符串的前 15 个字符追加到 dest
字符串的末尾1。希望这个解释对你有所帮助!
Final destination string : |This is destinationThis is source|
1.7 strncmp (比较)
strncmp
是 C 语言中的一个内置函数,用于比较两个字符串的前 n 个字符1。它的声明如下:
int strncmp(const char *str1, const char *str2, size_t n);
这里的参数是:
str1
:要比较的第一个字符串1。str2
:要比较的第二个字符串1。n
:要比较的字符的最大数量1。
strncmp
函数的返回值取决于比较的结果1:
- 如果
str1
小于str2
,则返回值小于 01。 - 如果
str1
大于str2
,则返回值大于 01。 - 如果
str1
等于str2
,则返回值等于 01。
这里的 “大于” 和 “小于” 是指在比较字符串时,按照字符的 ASCII 值进行比较1。
以下是一个 strncmp
函数的使用示例:
#include <stdio.h>
#include <string.h>int main() {char str1[15];char str2[15];int ret;strcpy(str1, "abcdef");strcpy(str2, "ABCDEF");ret = strncmp(str1, str2, 4);if(ret < 0) {printf("str1 is less than str2\n");} else if(ret > 0) {printf("str2 is less than str1\n");} else {printf("str1 is equal to str2\n");}return 0;
}
在这个例子中,我们比较了 str1
和 str2
的前 4 个字符。如果 str1
小于 str2
,我们就打印 “str1 is less than str2”,如果 str1
大于 str2
,我们就打印 “str2 is less than str1”,否则我们就打印 "str1 is equal to str2"1。
str2 is less than str1
1.8 strstr (查找)
函数用于在一个字符串中查找另一个字符串:
const char * strstr ( const char * str1, const char * str2 );char * strstr ( char * str1, const char * str2 );
12
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
/* strstr example */
#include <stdio.h>
#include <string.h>int main ()
{char str[] ="This is a simple string";char * pch;pch = strstr (str, "simple");strncpy (pch, "sample", 6);puts (str);return 0;
}
This is a sample string
#include <stdio.h>
#include <string.h>int main() {const char haystack[20] = "TutorialsPoint";const char needle[10] = "Point";char *ret;ret = strstr(haystack, needle);printf("The substring is: %s\n", ret);return(0);
}
The substring is: Point
1.9 strtok (分割)
函数用于分割字符串
char * strtok ( char * str, const char * sep );
1
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
/* strtok example */
#include <stdio.h>
#include <string.h>int main ()
{char str[] ="- This, a sample string.";char * pch;printf ("Splitting string \"%s\" into tokens:\n",str);pch = strtok (str," ,.-");while (pch != NULL){printf ("%s\n",pch);pch = strtok (NULL, " ,.-");}return 0;
}
Splitting string "- This, a sample string." into tokens:
This
a
sample
string
#include <stdio.h>
#include<cstring>
int main()
{const char *p = "zhangpengwei@bitedu.tech";const char* sep = ".@";char arr[30];char *str = NULL;strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容for(str=strtok(arr, sep); str != NULL; str=strtok(NULL, sep)){printf("%s\n", str);}
}
zhangpengwei
bitedu
tech
#include <string.h>
#include <stdio.h>int main() {char str[80] = "This is - www.tutorialspoint.com - website";const char s[2] = "-";char *token;/* 获取第一个子字符串 */token = strtok(str, s);/* 继续获取其他的子字符串 */while( token != NULL ) {printf( " %s\n", token );token = strtok(NULL, s);}return(0);
}
This iswww.tutorialspoint.comwebsite
1.10 strerror
char * strerror ( int errnum );
1
返回错误码,所对应的错误信息。
/* strerror example : error list */
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件int main ()
{FILE * pFile;pFile = fopen ("unexist.ent","r");if (pFile == NULL)printf ("Error opening file unexist.ent: %s\n",strerror(errno));//errno: Last error numberreturn 0;
}
Error opening file unexist.ent: No such file or directory
字符分类函数:
函数 如果他的参数符合下列条件就返回真
iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit 十进制数字 0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母az或AZ
isalnum 字母或者数字,az,AZ,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符
字符转换:
int tolower ( int c );
int toupper ( int c );/* isupper example */
#include <stdio.h>
#include <ctype.h>
int main ()
{int i=0;char str[]="Test String.\n";char c;while (str[i]){c=str[i];if (isupper(c)) c=tolower(c);putchar (c);i++;}return 0;
}
test string.
在C语言中,字符串是由字符组成的数组,以空字符(‘\0’)结束。while(str[i])
这个循环会一直执行,直到遇到字符串 str
的结束标志 ‘\0’。在ASCII编码中,‘\0’ 对应的整数值是0,所以当 str[i]
是 ‘\0’ 时,while(str[i])
的条件就不满足,循环就会结束。这就是 while(str[i])
的含义。希望这个解释对你有所帮助!
1.11 memcpy
void * memcpy ( void * destination, const void * source, size_t num );
1
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
memcpy 拷贝字符串和数组
/* memcpy example */
#include <stdio.h>
#include <string.h>struct {char name[40];int age;
} person, person_copy;int main ()
{char myname[] = "Pierre de Fermat";/* using memcpy to copy string: */memcpy ( person.name, myname, strlen(myname)+1 );person.age = 46;/* using memcpy to copy structure: */memcpy ( &person_copy, &person, sizeof(person) );printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );return 0;
}
注意事项
不适用于重叠复制
如果source和destination有任何的重叠,复制的结果都是未定义的。
#include <stdio.h>
#include <string.h>int main() {char str[] = "Hello, world!";// 尝试将 str 的前 5 个字符复制到 str 的第 3 个字符开始的位置memcpy(&str[2], str, 5);printf("%s\n", str);return 0;
}
在这个例子中,我们尝试将 str
的前 5 个字符(“Hello”)复制到 str
的第 3 个字符开始的位置。由于源地址和目标地址有重叠,所以复制的结果可能会出现错误。
当你运行这段代码时,可能会发现输出的字符串并不是你期望的 “HeHello, world!”,而是 “HeHeHo, world!”。这就是因为在复制过程中,源地址和目标地址的重叠部分被提前覆盖了。
为了避免这种情况,你可以使用 memmove
函数来代替 memcpy
。memmove
函数会正确处理源地址和目标地址的重叠,即使源地址和目标地址有重叠,它也能正确地复制数据。希望这个解释对你有所帮助!
为什么需要使用 memmove
“如果source和destination有任何的重叠,复制的结果都是未定义的”,这句话的意思是,如果你在使用 memcpy
函数时,源地址和目标地址有重叠的部分,那么复制的结果可能会出现错误。
这是因为 memcpy
在复制数据时,可能会先复制源地址的开始部分到目标地址,然后再复制源地址的结束部分。如果源地址和目标地址有重叠,那么在复制源地址的结束部分时,可能会覆盖已经复制到目标地址的数据,导致数据错误。
为了避免这种情况,你可以使用 memmove
函数来代替 memcpy
。memmove
函数会检查源地址和目标地址是否有重叠,如果有,它会从源地址的结束部分开始复制,这样就可以避免数据错误。希望这个解释对你有所帮助!
必须复制源字符串的 ‘\0’
需要注意的是,当使用 memcpy
复制字符串时,需要确保目标字符串有足够的空间来存储源字符串,并且在复制时要包括源字符串的结束符 ‘\0’。
在C语言中,字符串是由字符数组表示的,这个数组以特殊的字符 ‘\0’ 结束。‘\0’ 是一个空字符,用于标记字符串的结束。当我们使用字符串处理函数(如 printf
、strcpy
等)时,这些函数会依赖 ‘\0’ 来确定字符串何时结束。
因此,当我们使用 memcpy
复制字符串时,我们需要包括源字符串的 ‘\0’ 结束符,以确保目标字符串也有一个正确的结束标记。如果不复制 ‘\0’,那么目标字符串可能就没有正确的结束标记,这可能会导致未定义的行为,比如打印字符串时打印出预期之外的字符,或者在处理字符串时访问到不应该访问的内存区域。
所以,为了确保字符串的正确处理,我们在复制字符串时通常会包括 ‘\0’ 结束符。希望这个解释对你有所帮助!
1.12 memmove
memmove
是C语言中的一个库函数,用于在内存中移动数据1234。其函数原型为:
void *memmove (void *dest, const void *src, size_t n);
其中,dest
是目标内存区域,src
是源内存区域,n
是要复制的字节数1234。
这个函数的工作原理是将src
所指向的内存区域的n
个字节复制到dest
所指向的内存区域1234。如果目标区域和源区域有重叠的话,memmove
能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中1234。复制后源区域的内容会被更改1。如果目标区域与源区域没有重叠,则和memcpy
函数功能相同1。
以下是memmove
函数的一个使用示例1:
#include <stdio.h>
#include <string.h>int main () {const char src[] = "newstring";char dest[] = "oldstring";printf("Before memmove dest = %s, src = %s\n", dest, src);memmove(dest, src, 9);printf("After memmove dest = %s, src = %s\n", dest, src);return 0;
}
在这个示例中,src
字符串被复制到dest
字符串的位置,然后打印出最终的dest
字符串。这将产生以下结果:
Before memmove dest = oldstring, src = newstring
After memmove dest = newstring, src = newstring
需要注意的是,memmove
函数不会覆盖目标字符串1。此外,目标字符串空间必须足够大(足够容纳追加字符串src
的内容),并且目标空间必须可修改(前面不能加const并且不能说常量字符串)2。
和memcpy的差别 : 就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
1.13 memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
1
比较从ptr1和ptr2指针开始的num个字节
返回值如下:
<0 the first byte that does not match in both memory blocks has a lower value in ptr1 than in ptr2 (if evaluated as unsigned char values)
0 the contents of both memory blocks are equal
0 the first byte that does not match in both memory blocks has a greater value in ptr1 than in ptr2 (if evaluated as unsigned char values)
/* memcmp example */
#include <stdio.h>
#include <string.h>int main()
{char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";int n;n = memcmp(buffer1, buffer2, sizeof(buffer1));if (n > 0) {printf("'%s' is greater than '%s'.\n", buffer1, buffer2);}else if (n < 0) {printf("'%s' is less than '%s'.\n", buffer1, buffer2);}else {printf("'%s' is the same as '%s'.\n", buffer1, buffer2);}return 0;
}
memcmp 比较数组
#include <stdio.h>
#include <string.h>int main() {int arr1[] = {1, 2, 3, 4, 5};int arr2[] = {1, 2, 3, 4, 5};if (memcmp(arr1, arr2, sizeof(arr1)) == 0) {printf("The two arrays are equal.\n");} else {printf("The two arrays are not equal.\n");}return 0;
}
memcmp 比较结构体
#include <stdio.h>
#include <string.h>typedef struct {int id;char name[50];
} Person;int main() {Person p1 = {123, "Alice"};Person p2 = {123, "Alice"};if (memcmp(&p1, &p2, sizeof(Person)) == 0) {printf("The two persons are equal.\n");} else {printf("The two persons are not equal.\n");}return 0;
}
strcmp 和 memcmp 的区别
strcmp
和 memcmp
是两个用于比较数据的 C 语言函数,但它们的用途和行为有所不同1。
strcmp
是用于比较两个以空字符 ‘\0’ 结束的 C 字符串1。它会从两个字符串的开始处开始比较,直到遇到不同的字符或者遇到空字符 '\0’1。如果两个字符串在遇到空字符 ‘\0’ 之前都相同,那么strcmp
就会返回 0,表示两个字符串相等1。memcmp
则是用于比较两个指定长度的字节序列1。它并不关心数据是否以空字符 ‘\0’ 结束,只会简单地比较指定数量的字节1。因此,memcmp
可以用于比较任何类型的数据,包括字符串、结构体、数组等1。
总的来说,strcmp
和 memcmp
的主要区别在于,strcmp
是用于比较字符串的,而 memcmp
是用于比较任意类型的数据的1。希望这个解释对你有所帮助!
2. 库函数的模拟实现 (暂略)
2.1 模拟实现strlen
方式1:
//计数器方式
int my_strlen(const char * str)
{int count = 0;while(*str){count++;str++;}return count;
}
方式2:
//不能创建临时变量计数器
int my_strlen(const char * str)
{if(*str == '\0')return 0;elsereturn 1+my_strlen(str+1);//递归
}
方式3:
//指针-指针的方式
int my_strlen(char *s)
{char *p = s;while(*p != ‘\0’ )p++;return p-s;
}
2.2 模拟实现strcpy
参考代码:
//1.参数顺序
//2.函数的功能,停止条件
//3.assert
//4.const修饰指针
//5.函数返回值
//6.题目出自《高质量C/C++编程》书籍最后的试题部分char *my_strcpy(char *dest, const char*src)
{ char *ret = dest;//这里传进来的是字符串的首地址,即指针assert(dest != NULL);assert(src != NULL);while((*dest++ = *src++)){;}return ret;
}
2.3 模拟实现strcat
参考代码:
char *my_strcat(char *dest, const char*src)
{char *ret = dest;//同上,记住 不能返回局部变量的地址assert(dest != NULL);assert(src != NULL);while(*dest){dest++;}while((*dest++ = *src++)){;}return ret;
}
2.4 模拟实现strstr
char *strstr (const char * str1, const char * str2)
{char *cp = (char *) str1;char *s1, *s2;if ( !*str2 ){return((char *)str1);}while (*cp){s1 = cp;s2 = (char *) str2;while ( *s1 && *s2 && !(*s1-*s2) ){s1++, s2++;}if (!*s2)return(cp);cp++;}return(NULL);
}
/stackoverflow.com/questions/13095513/what-is-the-difference-between-memcmp-strcmp-and-strncmp-in-c)1。希望这个解释对你有所帮助!
2. 库函数的模拟实现 (暂略)
2.1 模拟实现strlen
方式1:
//计数器方式
int my_strlen(const char * str)
{int count = 0;while(*str){count++;str++;}return count;
}
方式2:
//不能创建临时变量计数器
int my_strlen(const char * str)
{if(*str == '\0')return 0;elsereturn 1+my_strlen(str+1);//递归
}
方式3:
//指针-指针的方式
int my_strlen(char *s)
{char *p = s;while(*p != ‘\0’ )p++;return p-s;
}
2.2 模拟实现strcpy
参考代码:
//1.参数顺序
//2.函数的功能,停止条件
//3.assert
//4.const修饰指针
//5.函数返回值
//6.题目出自《高质量C/C++编程》书籍最后的试题部分char *my_strcpy(char *dest, const char*src)
{ char *ret = dest;//这里传进来的是字符串的首地址,即指针assert(dest != NULL);assert(src != NULL);while((*dest++ = *src++)){;}return ret;
}
2.3 模拟实现strcat
参考代码:
char *my_strcat(char *dest, const char*src)
{char *ret = dest;//同上,记住 不能返回局部变量的地址assert(dest != NULL);assert(src != NULL);while(*dest){dest++;}while((*dest++ = *src++)){;}return ret;
}
2.4 模拟实现strstr
char *strstr (const char * str1, const char * str2)
{char *cp = (char *) str1;char *s1, *s2;if ( !*str2 ){return((char *)str1);}while (*cp){s1 = cp;s2 = (char *) str2;while ( *s1 && *s2 && !(*s1-*s2) ){s1++, s2++;}if (!*s2)return(cp);cp++;}return(NULL);
}