1c语言基础

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. 修饰局部变量-称为静态局部变量
  2. 修饰全局变量-称为静态全局变量
  3. 修饰函数-称为静态函数
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函数可以用来输入longlong longshort类型的数据。下面是一些示例:

#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分别用于输入longlong longshort类型的数据。&符号是取地址运算符,它的作用是获取变量的内存地址。scanf函数需要这个地址来改变变量的值。注意,scanf函数的使用需要谨慎,因为它可能会导致缓冲区溢出等问题。在实际编程中,建议使用更安全的输入函数,如fgetssscanf


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。这个约束使得字符串可以被一些特定的函数(如printfstrlen)用于特定的目的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。而且,srcdest字符串中都必须包含字符’\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;
}

在这个例子中,我们定义了两个字符数组 str1str2,然后使用 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;
}

在这个例子中,我们比较了 str1str2 的前 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 函数来代替 memcpymemmove 函数会正确处理源地址和目标地址的重叠,即使源地址和目标地址有重叠,它也能正确地复制数据。希望这个解释对你有所帮助!


为什么需要使用 memmove

“如果source和destination有任何的重叠,复制的结果都是未定义的”,这句话的意思是,如果你在使用 memcpy 函数时,源地址和目标地址有重叠的部分,那么复制的结果可能会出现错误。

这是因为 memcpy 在复制数据时,可能会先复制源地址的开始部分到目标地址,然后再复制源地址的结束部分。如果源地址和目标地址有重叠,那么在复制源地址的结束部分时,可能会覆盖已经复制到目标地址的数据,导致数据错误。

为了避免这种情况,你可以使用 memmove 函数来代替 memcpymemmove 函数会检查源地址和目标地址是否有重叠,如果有,它会从源地址的结束部分开始复制,这样就可以避免数据错误。希望这个解释对你有所帮助!


必须复制源字符串的 ‘\0’

需要注意的是,当使用 memcpy 复制字符串时,需要确保目标字符串有足够的空间来存储源字符串,并且在复制时要包括源字符串的结束符 ‘\0’。

在C语言中,字符串是由字符数组表示的,这个数组以特殊的字符 ‘\0’ 结束。‘\0’ 是一个空字符,用于标记字符串的结束。当我们使用字符串处理函数(如 printfstrcpy 等)时,这些函数会依赖 ‘\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 的区别

strcmpmemcmp 是两个用于比较数据的 C 语言函数,但它们的用途和行为有所不同1。

  • strcmp 是用于比较两个以空字符 ‘\0’ 结束的 C 字符串1。它会从两个字符串的开始处开始比较,直到遇到不同的字符或者遇到空字符 '\0’1。如果两个字符串在遇到空字符 ‘\0’ 之前都相同,那么 strcmp 就会返回 0,表示两个字符串相等1。
  • memcmp 则是用于比较两个指定长度的字节序列1。它并不关心数据是否以空字符 ‘\0’ 结束,只会简单地比较指定数量的字节1。因此,memcmp 可以用于比较任何类型的数据,包括字符串、结构体、数组等1。

总的来说,strcmpmemcmp 的主要区别在于,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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1554751.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

[SAP ABAP] 数据元素添加参数ID(Parameter ID)

学生表(ZDBT_STU_437) 示例&#xff1a;为学生表ZDBT_STU_437中的数据元素ZDE_STUID_437创建Parameter ID 1.使用事务码SM30维护TPARA表 新建参数ID并输入简短描述 点击保存按钮&#xff0c;选择指定的包即可生成参数ID 2.参数ID和数据元素绑定 使用SE11对学生表(ZDBT_STU_…

自动驾驶系列—颠覆未来驾驶:深入解析自动驾驶线控转向系统技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

终于知道神秘的蓝莓真身了

黑森林实验室&#xff08;Black Forest Labs&#xff0c;简称 BFL&#xff09;是一家初创公司&#xff0c;由流行的 Stable Diffusion AI 图像生成模型的创建者创立&#xff0c;该模型是许多 AI 图像生成应用程序和服务&#xff08;如 Midjourney&#xff09;的基础。 这意味着…

模拟实现string

1.代码理解 1.substr 断言指定的位置在字符的长度之内&#xff0c;_size-len是剩余字符的长度(pos后面的),如果输入len是大于pos后面的字符长度则默认为pos后面全部的字符去拷贝&#xff0c;再建立一个sub去储存&#xff0c;通过循环把pos后面的字符接收到sub里面。 string …

新闻推荐系统:Spring Boot的创新应用

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

win11远程连接MySQL(linux版),不需安装docker容器

不想安装虚拟机&#xff0c;想在Windows 11上运行Linux。 在win11的搜索框内&#xff0c;搜索"启用或关闭"&#xff0c;出现了“启用或关闭Windows功能”&#xff0c;双击打开。 勾选"适用于Linux的Windows子系统"&#xff0c;“虚拟机平台”&#xff0c…

yolov8/9/10/11模型在食品图像商标检测中的应用【代码+数据集+python环境+GUI系统】

yolov8/9/10/11模型在食品图像商标检测中的应用【代码数据集python环境GUI系统】 背景意义 准确的食品商标检测能确保消费者购买到的食品来源可靠、信息真实。消费者可以通过商标了解食品的生产厂家、生产日期、成分等关键信息&#xff0c;从而判断食品的安全性和质量。如果商…

Linux系统字符命令关机方法对比

一、相同点&#xff1a;都可以达到关机或重启系统的目的。 二、不同点&#xff1a;命令内部的工作过程不同。 1、shutdown 安全的关机命令&#xff1a;系统管理员会通知所有登录的用户系统将要关闭且 login 指令会被冻结&#xff0c;即新的用户不能再登录。根据使用的参数不同…

YOLO11改进|注意力机制篇|引入HAT超分辨率重建模块

目录 一、HAttention注意力机制1.1HAttention注意力介绍1.2HAT核心代码 二、添加HAT注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、HAttention注意力机制 1.1HAttention注意力介绍 HAT模型 通过结合卷积特征提取与多尺度注意…

C++系列-二叉搜索树

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 二叉搜索树 二叉搜索树又称二叉排序树&#xff0c;它或者是一颗空树&#xff0c;或者是具有以下性质的树 若它的左子树不为空&#xff0c;则左子树上的所有节点的值都小于根节点…

【ubuntu】apt是什么

目录 1.apt简介 2.常用apt指令 2.1安装 2.2更新列表 2.3更新已经安装的软件包 2.4搜索软件包 2.5显示软件包信息 2.6移除软件包 2.7清理无用的安装包 2.8清理无用的依赖项 3.apt和apt-get 3.1区别 3.2 总结 1.apt简介 apt的全称是advanced package …

7c结构体

文章目录 一、结构体的设计二、结构体变量的初始化2.1结构体在内存表示&#xff1b;**2.2**结构体类型声明和 结构体变量的定义和初始化只声明结构体类型声明类型的同时定义变量p1用已有结构体类型定义结构体变量p2*定义变量的同时赋初值。*匿名声明结构体类型 2.3 结构体嵌套及…

Ubuntu安装Hadoop3.4

1、创建Hadoop用户 sudo adduser hadoop 将Hadoop加进sudo用户组,赋予更高权限: sudo usermod -G sudo hadoop 3、安装JDK(略) 查看JDK安装路径:which java 和 ls -al 3、配置SSH免密登录 在Hadoop分布式集群环境中,各个机器之间的通信通常需要使用SSH的方式进行连…

MIT6.824--入门介绍

分布式系统的定义 分布式系统是指将多部各自拥有内存与时钟等硬件设备的独立的计算机系统以网络汇集起来&#xff0c;彼此之间仅仅通过网络消息传递进行通信和协调&#xff0c;共同对外提供服务&#xff0c;但对于系统的用户来说&#xff0c;就像是一台计算机在提供服务一样。…

Python+Matplotlib可视化初等函数示例

import numpy as np import matplotlib.pyplot as pltplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] Falsefig, axs plt.subplots(2, 3, figsize(15, 10))# 1. 幂函数 x np.linspace(-2, 2, 200) axs[0, 0].plot(x, x**2, labely x^2) axs[0,…

Python-初识Python

前言&#xff1a;在这篇博客当中&#xff0c;我们将步入Python知识的殿堂&#xff0c;Python以其简单、易学、开发效率高在近些年的发展可谓是迅猛&#xff0c;在许多领域都可以见到它的场景&#xff0c;例如&#xff1a;人工智能/机器学习、大数据开发、后端开发等都会用到。 …

绝对值得收藏!分享7款ai写作论文免费一键生成网站

在当前的学术研究和写作过程中&#xff0c;AI写作工具已经成为了许多研究者和学生的重要助手。这些工具不仅能够提高写作效率&#xff0c;还能帮助生成高质量的论文内容。以下是七款免费的AI写作论文生成器&#xff0c;其中特别推荐千笔-AIPassPaper。 1.千笔-AIPassPaper 千…

第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第二套)

一.题目分析 &#xff08;1&#xff09;.题目 &#xff08;2&#xff09;.题目分析 1.按键功能分析 a.B1界面切换 b.B2每次按下R加2 c.B3每次按下R减2 d.LED控制按键 2.信号输出功能分析 a.PA7信号输出-----信号的输出就需要使用到输入捕获和输出比较功能 b.输出信号的…

融媒体服务中PBO进行多重采样抗锯齿(MSAA)

如果不理解pbo 那先去了解概念&#xff0c;在此不再解释&#xff0c;这是我为了做融合服务器viewpointserver做的一部分工作&#xff0c;融合服务器的功能是将三维和流媒体&#xff0c;AI融合在一起&#xff0c;viewpointserver会直接读取三维工程的文件&#xff0c;同时融合rt…

BUCK的工作模式:CCM BCM DCM

连续导通模式CCM CCM工作状态 一个开关周期内&#xff0c;电感内的电流始终大于0&#xff0c;电感在DCDC整个工作周期中一直处于充电放电状态&#xff1b;即使DCDC的功率开关管断开时&#xff08;Toff&#xff09;&#xff0c;电感中一直有电流流过&#xff1b; 功率开关管闭合…