当前位置: 首页 > news >正文

由浅到深认识C语言(10):字符串处理函数

该文章Github地址:https://github.com/AntonyCheng/c-notes

在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn.net/AntonyCheng/article/details/136555245),该模板集成了最常见的开发组件,同时基于修改配置文件实现组件的装载,除了这些,模板中还有非常丰富的整合示例,同时单体架构也非常适合SpringBoot框架入门,如果觉得有意义或者有帮助,欢迎Star & Issues & PR!

上一章:由浅到深认识C语言(9):动态内存分配

10.字符串处理函数

对字符串的处理一般在嵌入式编程,应用编程和网络编程中会大量用到;

字符串主要有拷贝,连接,比较,切割,变换等操作;

要求熟练使用常见字符串处理函数,并且会编写典型的字符串操作函数;

字符串操作函数有如下:

  • strlen 长度的测量;
  • strcpy/strncpy 字符串拷贝;
  • strcat/strncat 字符串连接;
  • strcmp/strncmp 字符串比较;

字符串头文件: #include<string.h>

关于 Visual Studio 的安全模式需要补充的解释,在 Visual Studio 中默认的一些输入输出操作均为安全模式下的操作,即 函数名_s 的结构,该结构对于字符串来说影响极大,因为字符串的长度是不好把控的,很容易造成内存的溢出,所以为了解决此问题,在相关函数操作字符串时,会跟一个限制大小的 int 参数,即接下来操作的字符数组(字符串)的长度不能超过该长度;

10.1.测字符串长度函数

原型: int strlen(const char *str)

参数说明: str 是被测量的字符串首元素地址,const 是一个关键字,在 10.9 中会有介绍;

返回值: 返回字符串的长度,不包含 \0 和 null 字符;

这个函数虽然传的是 * 类型,但是不会修改其指向的内容;

示例如下:

#include<stdio.h>
#include<string.h>void test() {char buf1[128] = "hello world";char buf2[] = "hello world";char buf3[] = "hello\0world";char buf4[] = "hello\123\\world";printf("sizeof(buf1) = %d\n", sizeof(buf1));printf("strlen(buf1) = %d\n", strlen(buf1));printf("sizeof(buf2) = %d\n", sizeof(buf2));printf("strlen(buf2) = %d\n", strlen(buf2));printf("sizeof(buf3) = %d\n", sizeof(buf3));printf("strlen(buf3) = %d\n", strlen(buf3));printf("sizeof(buf4) = %d\n", sizeof(buf4));//hello--5个 \123--1个 \\--1个 world--5个 \0--1个printf("strlen(buf4) = %d\n", strlen(buf4));//hello--5个 \123--1个 \\--1个 world--5个 return;
}int main(int argc, char* argv[]) {test();return;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

sizeof 和 strlen 的主要区别

sizeof 计算的是字符串中所有的元素,且加上最后一位隐藏掉的 \0

strlen 计算的是字符串中可见范围的元素,遇到 \0 就结束,不管其是否是最后的隐藏位;

实例:重写函数

自己定义一个 strlen 函数测量字符串长度;

#include<stdio.h>
#include<string.h>int my_strlen(char* p) {int count = 0;int turn = 1;while (turn) {if (*(p + count) != '\0') {count++;}else {turn = 0;}}return count;
}int main(int argc, char* argv[]) {char ch[] = "hello\123\\ world";int length1 = strlen(ch);int length2 = my_strlen(ch);printf("strlen = %d\n", length1);printf("my_strlen = %d", length2);return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.2.字符串拷贝函数

原型一: char* strcpy(char* dest,const char* src)

  • 功能:把 src 所指向的字符串赋值到 dest 所指向的空间中;
  • 返回值:返回 dest 字符串的首地址;
  • 注意:遇到 \0 会结束,只是 \0 也会被拷贝过去,需要保证 dest 足够大

示例如下:

由于 VS 中该函数存在安全问题,所以需要按照编译器给定的安全模式输出,参数会不一样;

#include<stdio.h>
#include<string.h>void test() {char src[] = "hello world";char dest[128] = "";strcpy_s(dest,128 ,src); //安全模式下,还需要在中间插入需要传的字节大小,我们直接使用dest的字节大小printf("dest = %s", dest);
}int main(int argc, char* argv[]) {test();return;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

\0 的情况:

#include<stdio.h>
#include<string.h>void test() {char src[] = "hello\0world";char dest[128] = "";strcpy_s(dest,128 ,src);printf("dest = %s", dest);
}int main(int argc, char* argv[]) {test();return;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原型二: char* strncpy(char* dest,const char* src,int num)

  • 功能:把 src 所指向的字符串赋值到 dest 所指向的空间中;
  • 参数说明:num 表示从第一位字符开始所拷贝字符的个数,若数字过大,则遇 \0 结束
  • 返回值:返回 dest 字符串的首地址;
  • 注意:遇到 \0 会结束,但是 \0 不会被拷贝过去,需要保证 dest 足够大

实例:重写函数

自己定义一个 strcpy 函数来拷贝字符串;

#include<stdio.h>
#include<string.h>void my_strcpy(char* p1,char* p2) {int i = 0;int turn = 1;while (turn) {if (*(p2 + i) != '\0') {*(p1 + i) = *(p2 + i);i++;}else {*(p1 + i) = '\0';turn = 0;}}
}int main(int argc, char* argv[]) {char ch_1[] = "hello wo\0rld";char ch_2[128] = "";char ch_3[128] = "";my_strcpy(ch_2, ch_1);printf("sizeof(ch_2) = %d\n", sizeof(ch_2));printf("%s\n", ch_2);strcpy_s(ch_3,128,ch_1);printf("sizeof(ch_3) = %d\n", sizeof(ch_3));printf("%s\n", ch_3);return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.3.字符串拼接函数

原型一:char* strcat(char* dest,const char* src);

  • **功能:**将 src 的字符串拼接到 dest 的末尾(这里指的是 dest 中第一个 \0),且需要 dest 有足够的空间;

  • 示例如下:

    由于 VS 中该函数存在安全问题,所以需要按照编译器给定的安全模式输出,参数会不一样;

    #include<stdio.h>
    #include<string.h>void test() {char ch1[] = "world";char ch2[128] = "hello ";strcat_s(ch2 ,128 ,ch1);printf("%s", ch2);
    }int main(int argc, char* argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原型二:char* strncat(char* dest,const char* src,int num);

  • **功能:**将 src 的字符串中的前 n 个字符拼接到 dest 的末尾(这里指的是 dest 中第一个 \0),且需要 dest 有足够的空间;

  • 示例如下:

    #include<stdio.h>
    #include<string.h>void test() {char ch1[] = "world";char ch2[128] = "hello ";strncat_s(ch2, 123, ch1, 3);printf("%s", ch2);
    }int main(int argc, char* argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实例:重写函数

自己定义一个 strcat 函数来连接字符串;

#include<stdio.h>
#include<string.h>void my_strcat(char* p2, char* p1) {int count = 0;while (*(p2 + count) != '\0') {count++;}int i = 0;int b = 1;while (b) {if (*(p1 + i) != '\0') {*((p2 + count) + i) = *(p1 + i);i++;}else {b = 0;}}
}int main(int argc, char* argv[]) {char ch1[] = "hello\0 world";char ch2[128] = "hello C ";char ch3[128] = "hello C ";my_strcat(ch2, ch1);strcat_s(ch3, 128, ch1);printf("ch2 = %s\n", ch2);printf("ch3 = %s", ch3);return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.4.字符串比较函数

原型一:int strcmp(const char* s1,const char* s2);

  • **功能:**将 s1 和 s2 指向的字符串逐个字符进行比较,用于整个字符串的比较

  • 返回值:

    • >0(一般是 1 ) 表示 s1 > s2 即两字符串不相同

    • <0(一般是 -1 ) 表示 s1 < s2 即两字符串不相同

    • =0 表示 s1 = s2 即两字符串相同

  • 示例如下:

    #include<stdio.h>
    #include<string.h>void test() {char s1[] = "hello world";char s2[] = "hello zorld";char s3[] = "hello aorld";char s4[] = "hello world";printf("'w' or 'z' = %d\n",strcmp(s1, s2));printf("'w' or 'a' = %d\n",strcmp(s1, s3));printf("'w' or 'w' = %d\n",strcmp(s1, s4));return;
    }int main(int argc, char* argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原型二:int strncmp(const char* s1,const char* s2,int num);

  • **功能:**将 s1 和 s2 指向的字符串前 n 个字符逐个进行比较,用于部分字符串的比较

  • 返回值:

    • >0(一般是 1 ) 表示 s1 > s2 即两部分字符串不相同

    • <0(一般是 -1 ) 表示 s1 < s2 即两部分字符串不相同

    • =0 表示 s1 = s2 即两部分字符串相同

实例:重写函数

自己定义一个 strcmp 函数来比较字符串;

#include<stdio.h>
#include<string.h>int my_strcmp(char* str1, char* str2) {int count1 = 0;int count2 = 0;int length1 = 0;int length2 = 0;int i1 = 0;int i2 = 0;while (*(str1 + i1) != '\0') {i1++;length1++;}while (*(str2 + i2) != '\0') {i2++;length2++;}i1 = 0;i2 = 0;if (length1==length2) {while (*(str1 + i1) != '\0') {if (*(str1 + i1) > *(str2 + i2)) {return 1;}if (*(str1 + i1) < *(str2 + i2)) {return -1;}i1++;i2++;}return 0;}else if (length1 > length2) {while (*(str2 + i2) != '\0') {if (*(str1 + i1) > *(str2 + i2)) {return 1;}if (*(str1 + i1) < *(str2 + i2)) {return -1;}i1++;i2++;}return 1;}else {while (*(str1 + i1) != '\0') {if (*(str1 + i1) > *(str2 + i2)) {return 1;}if (*(str1 + i1) < *(str2 + i2)) {return -1;}i1++;i2++;}return -1;}
}void test() {char str1[] = "abc";char str2[] = "abc";char str3[] = "aac";char str4[] = "acc";char str5[] = "abca";char str6[] = "aaca";printf("abc abc = %d\n", strcmp(str1, str2));printf("abc abc = %d\n", my_strcmp(str1, str2));printf("abc aac = %d\n", strcmp(str1, str3));printf("abc aac = %d\n", my_strcmp(str1, str3));printf("abc acc = %d\n", strcmp(str1, str4));printf("abc acc = %d\n", my_strcmp(str1, str4));printf("abc abca = %d\n", strcmp(str1, str5));printf("abc abca = %d\n", my_strcmp(str1, str5));printf("abc aaca = %d\n", strcmp(str1, str6));printf("abc aaca = %d\n", my_strcmp(str1, str6));
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.5.字符串变换函数

strchr字符查找

原型:char* strchr(const char* str1,char ch);

**功能:**在字符串 str1 中查找字符 ch 出现的的地址;

**返回值:**返回 ch 第一次出现的位置地址,找不到则返回空值

示例如下:

我们运用 strchr 函数将 “hello world” 里的 ‘l’ 全部替换为 ‘a’;

#include<stdio.h>
#include<string.h>void test() {char str[] = "hello world";char* ret = NULL;while (1) {ret = strchr(str, 'l');if (ret == NULL) {break;}*ret = 'a';}printf("%s", str);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

strstr字符串查找

原型:char* strstr(const char* src,const char* dest);

**功能:**在字符串 src 中查找 dest 字符串出现的地址;

**返回值:**返回 dest 第一次出现的位置地址,找不到则返回空值

示例如下:

利用 strstr 函数将 “www.dog.cat.dog.com” 中的 “dog” 用 * 屏蔽掉;

#include<stdio.h>
#include<string.h>void test() {char str1[] = "www.dog.cat.dog.com";char* ret = NULL;while (1) {ret = strstr(str1, "dog");if (ret == NULL) {break;}memset(ret, '#', strlen("dog"));}printf("%s", str1);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.6.字符串处理函数

memset字符填充

原型:void* memset(void* str,char c,int n);

**功能:**将 str 所指向的内存区的前 n 个全部用 c 填充,用于清楚指定空间,常用于 malloc 申请空间后的初始化操作;

**返回值:**返回 str 的地址;

atoi/atol/atof字符转换

头文件:#include<stdlib.h>

原型:

int atoi(const char* str);

long atol(const char* str);

double atof(const char* str);

功能:

str 所指向的 数字字符串转化为 int/long/double

示例如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>void test() {char str1[] = "123.456";char str2[] = "123";printf("%f\n", (double)atoi(str2)+atof(str1));
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实例:重写函数

自己定义一个 strcmp 函数来比较字符串;

#include<stdio.h>int my_atoi(char* str) {int sum = 0;while (*str != '\0'&& *str >= '0' && *str <= '9') {sum = sum * 10 + *str - '0';//这里要理解 ascii 中字符数与字符0的差值就是真实的数值str++;}return sum;
}int main(int argc, char* argv[]) {char str[] = "123";printf("%d\n", my_atoi(str) + 1);return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

strtok字符串切割

原型:char* strtok(char* str,const char* string);

**功能:**strtok 函数可以将一个字符串 sstring字符串==(字符串就代表要用双引号包裹)==为分界线进行切割;

注意:当函数在参数 s 字符串中发现参数 string 中包含的分割字符时,则会将该字符改为 \0 字符,当连续出现多个时只会替换第一个为 \0,即调用一次只能切割一次,在第二次调用时,string 字符串不变,s 字符串一定要设置成 NULL,因为第一次返回的地址就是下一次要切割的地址,传空值就能够接着该地址往下切割,每次调用成功则返回指向被分割出片段的指针,一般将切割后的独立字符串首地址存放在指针数组中

这也是唯一一个要对原字符串进行改变的函数;

示例如下:

在 Visual Studio 中,该函数是一个安全性问题函数

char *strtok_s( char *strToken, const char *strDelimit, char **buf);最后一个参数表示将剩余的字符串存储在 buf 指针数组中,而不是静态变量中,从而保证了安全性。

#include<stdio.h>
#include<string.h>int test() {char str[] = { "xixixi,hehehe,lalala" };char* protect[] = { NULL };char* arr[32] = { NULL };//第一次切割int i = 0;arr[i] = strtok_s(str, ",", protect);//第二次……切割while (arr[i] != NULL) {i++;arr[i] = strtok_s(NULL, ",", protect);}//输出结果i = 0;while (arr[i] != NULL) {printf("第%d次输出为:\n", i + 1);printf("%s\n", arr[i]);i++;}printf("原函数为:%s", str);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.7.sprintf组包

图解 printf:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**意义:**将零散的数据组成一个完整的整体;

格式:

int sprintf(str,"格式",数据);
//str:用于存放组好的报文
//"格式":按照格式组包
//数据:各个零散的数据
//返回值:返回的是组好的报文的实际长度(不包含 \0)

示例一如下:

在 Visual Studio 中,该函数是一个安全性问题的函数,需要在第二个参数中加入适当的长度限制,保证内存不溢出,通常可以直接使用存放该报文的数组长度;

#include<stdio.h>void test() {int year = 2022;int mon = 3;int day = 24;//要求:将以上变量组成一个"2022年3月24日"字符串char str[128] = "";sprintf_s(str, 128, "%d年%d月%d日", year, mon, day);printf("%s\n", str);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例二如下:

#include<stdio.h>void test() {char name[] = "陈一";int age = 20;char sex[] = "男";char addr[] = "四川省";char str[128] = "";sprintf_s(str, 128, "姓名:%s;年龄:%d;性别:%s;地址:%s", name, age, sex, addr);printf("%s\n", str);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例三如下:

将 int 型整数转换成字符串;

#include<stdio.h>void test() {int a = 100;int b = 200;char str[128] = "";sprintf_s(str, 128, "%d + %d = %d", a, b, a + b);printf("%s\n", str);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.8.sscanf解包

图解 scanf:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**意义:**将一个完整的整体拆解成零散的数据;

格式:

int sscanf(str,"格式",地址数据);
//str:存放报文的字符串地址
//"格式":按照格式组包
//数据:各个零散的需要获取数据的地址

注意:sscanf若遇到不满足条件的字符(条件的反面)时,会直接结束获取进程,即使该字符后有满足的字符;

示例如下:

#include<stdio.h>void test() {char str[] = "2022年3月24日";int year = 0;int mon = 0;int day = 0;sscanf_s(str, "%d年%d月%d日", &year, &mon, &day);printf("%d , %d , %d\n", year, mon, day);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

要注意提取的格式,%c%d%f%lf 的提取类似,但是 %s 提取字符串就会有问题,因为提取的原理是对于字符数组进行逐个遍历,寻找适合要求的部分并进行取值,例如 %d 的范围是 1-9%c 的范围是单个字符%f 的范围是 float型%lf 的范围是 double型,而如果我们要遍历一个字符串的话,它的范围就是所有字符(但会遇到空格结束),但是遍历内容全是字符,所以总是会拿到所有的内容,而不是我们想要的内容;

高级用法

用法一:使用 %*s%*d 跳过提取内容,即不要提取到的内容;

%*s%s 的相同点在于都能提到数据,不同点在于前者不能被取到,而后者可以被取到,即能够拿出来赋值;

示例如下:提取 "1234 5678" 中的 "5678"

#include<stdio.h>void test() {int data1 = 0;sscanf_s("1234 5678", "%*d %d", &data1);//也可以这样//sscanf_s("1234 5678", "1234 %d", &data1);printf("data1 = %d\n", data1);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以我们可以用以上的方法和 %s 的特性来做一些复杂的操作:

提取 "abcdef 1234 sndhcbakdh 8763 ds 6.999" 中的数学数据;

#include<stdio.h>void test() {int data1 = 0;int data2 = 0;float data3 = 0;sscanf_s("abcdef 1234 sndhcbakdh 8763 ds 6.999", "%*s %d %*s %d %*s %f", &data1, &data2, &data3);printf("data1 = %d\n", data1);printf("data2 = %d\n", data2);printf("data3 = %f\n", data3);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用法二:使用 %ns%nd 来提取指定宽度 n 的字符串或数据;

示例如下:

#include<stdio.h>void test() {int data = 0;sscanf_s("12abc5678", "%*5s%d", &data);printf("data = %d\n", data);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**用法三:**支持集合的操作(正则表达式);

  • %[a-z]:提取 a-z 的字符串;

    示例如下:

    #include<stdio.h>void test() {char str[16] = "";sscanf_s("abcDeFgH","%[a-z]",str,16);printf("str = %s\n", str);
    }int main(int argc, char* argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • %[aBc]:提取 aBc 中的任意一个;

    示例如下:

    #include<stdio.h>void test() {char str[16] = "";sscanf_s("aBcBcacBefacc","%[aBc]",str,16);printf("str = %s\n", str);
    }int main(int argc, char* argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • %[^abc]:提取非 abc 中任何一个的字符;

    示例如下:

    #include<stdio.h>void test() {char str[16] = "";sscanf_s("ABCabcDeFgH", "%[^abc]", str, 16);printf("str = %s\n", str);
    }int main(int argc, char* argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

高级用法案例

案例一:

需求: 现在有 data=0str[16]="" ,需要从 “12345678” 中提取到 34 赋值给整型 data,再提取到 78 赋值给字符串 str ,一并输出;

#include<stdio.h>void test() {int data = 0;char str[16] = "";sscanf_s("12345678", "%*2d%2d%*2d%s", &data, str, 3);printf("data = %d\n", data);printf("str = %s\n", str);}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

案例二:

需求:给定一个形如 xxx@xxx.com 的网址,其中 xxx 不定长,提取两部分 xxx 分别放入两个字符数组中;

#include<stdio.h>void test() {char str1[16] = "";char str2[16] = "";sscanf_s("1999998888@qq.com", "%[^@]%*[@]%[^.]", str1, 16, str2, 16);printf("str1 = %s\n", str1);printf("str2 = %s\n", str2);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

案例三:

需求:在歌词格式中里提取歌词时间(不要毫秒)和内容(格式:[分:秒:毫秒][分:秒:毫秒]歌词内容);

#include<stdio.h>void test() {int minute1 = 0;int second1 = 0;int minute2 = 0;int second2 = 0;char song[128] = "";char file[] = "[1:13:46][2:11:53]这是歌词的内容!";sscanf_s(file, "[%d:%d:%*d][%d:%d:%*d]%s", &minute1, &second1, &minute2, &second2, song, 128);printf("[%d:%d—%d:%d]:%s", minute1, second1, minute2, second2, song);}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.9.const关键字

const修饰普通变量

const修饰普通变量,表示该普通变量只读,即该变量只能取值,不能被赋值,类似于 Java 中的 final,但是有区别,Java中是绝对不能改变的一个值,但是在C语言中,在编译器固定只读变量地址的前提下,如果知道其地址,这个值也是可以改的,被 const 赋值后的变量指针类型为 const int* 类型,我们只需要将其强制转换成 int* 即可;

const修饰的变量一定要初始化,虽然可以通过地址类型的强制转换去更改内容,但是尽量不要去做;

只读示例如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

改变权限示例如下:

#include<stdio.h>void test() {const num = 10;//将指针类型从const int*强制转换为int*,然后再取内容进行赋值*(int*)&num = 11;printf("num = %d\n", num);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以从底层来讲,该变量不能被修改的原因是此变量指针的指针类型不可重新指向另外的地址,只需要强制转换成可重定向的指针类型,就能临时性地永久改变该地址的内容(临时性指的是下一次需要改变时仍要重新强制转换,永久指的是从地址层改变该地址的内容);

const修饰指针星花

格式:const int* p

const修饰指针星花重在修饰 * 而不是 p ,所以修饰之后的效果是用户不能借助 *p 更改空间的内容,但是 p 可以指向其他空间,即 *p 只读,p 可读可写

*p 只读示例如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

p 可读可写示例如下:

#include<stdio.h>void test() {int num = 10;const int* p = &num;int num2 = 20;p = &num2;printf("num = %d\n", *p);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

const修饰指针变量

格式:int* const p

const修饰指针变量重在修饰 p ,而不是 * ,所以修饰后的效果是用户可以通过 *p 修改 p 的所指向空间的内容,但是不能再更改 p 的指向,即 *p 可读可写,p 只读

p 只读示例如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*p 可读可写示例如下:

#include<stdio.h>void test() {int num1 = 10;int* const p = &num1;*p = 20;printf("num = %d\n", *p);
}int main(int argc, char* argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

const同时修饰指针星花和变量

格式:const int* const p

从上两点可以看出,此时 *p 和 p 都是只读的

*p 只读示例如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

p 只读示例如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相关文章:

由浅到深认识C语言(10):字符串处理函数

该文章Github地址&#xff1a;https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.csdn…...

防范服务器被攻击:查询IP地址的重要性与方法

在当今数字化时代&#xff0c;服务器扮演着重要的角色&#xff0c;为企业、组织和个人提供各种网络服务。然而&#xff0c;服务器也成为了网络攻击者的目标之一&#xff0c;可能面临各种安全威胁&#xff0c;例如DDoS攻击、恶意软件攻击、数据泄露等。为了有效地防范服务器被攻…...

3. ElasticSearch搜索技术深入与聚合查询实战

1. ES分词器详解 1.1 基本概念 分词器官方称之为文本分析器&#xff0c;顾名思义&#xff0c;是对文本进行分析处理的一种手段&#xff0c;基本处理逻辑为按照预先制定的分词规则&#xff0c;把原始文档分割成若干更小粒度的词项&#xff0c;粒度大小取决于分词器规则。 1.2 …...

【Linux】Shell编程【一】

shell是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。 Shell 是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问操作系统内核的服务。 Shell属于内置的…...

Windows10+tensorrt+python部署yolov5

一、安装cuda 打开NVIDIA控制面板 —>帮助—>系统信息—>组件&#xff0c;找到驱动版本新&#xff0c;我这边是11.2&#xff0c; 然后去CUDA Toolkit Archive | NVIDIA Developer下载对应版本的CUDA&#xff0c;根据查看的CUDA型号确定对应的cuda Toolhit版本&#…...

【前端框架的发展史详细介绍】

前端框架的发展史 前端框架的发展史可以追溯到1995年&#xff0c;当时微软推出了IE浏览器并开始支持CSS&#xff0c;随后&#xff0c;在1997年&#xff0c;W3C&#xff08;万维网联盟&#xff09;发布了CSS的第一个正式标准。 在2003年&#xff0c;苹果推出了Safari浏览器&am…...

[JAVAEE]—进程和多线程的认识

文章目录 什么是线程什么是进程进程的组成什么是pcb 进程概括线程线程与进程的关系线程的特点 创建线程创建线程方法创建线程的第二种方法对比 其他的方式匿名内部类创建线程匿名内部类创建Runable的子类lambda表达式创建一个线程 多线程的优势 什么是线程 什么是进程 首先想…...

sqllab第十九关通关笔记

知识点&#xff1a; 错误注入 最大长度为32位&#xff1b;如果目标长度>32时&#xff0c;需要利用截取函数进行分段读取referer注入 insert语句update语句 通过admin admin进行登录发现页面打印除了referer字段的信息 这应该是一个referer注入 首先进行测试一下 构造payl…...

张量维度改变总结

文章目录 一、view() 或 reshape()二、unsqueeze()三、squeeze()四、transpose()五、torch.expand_dims 一、view() 或 reshape() view() 或 reshape(): 这两个函数可以用于改变张量的形状&#xff0c;但保持元素总数不变。它们可以接受一个新的形状作为参数&#xff0c;并返回…...

C++ ezOptionParse的用法

在网上找了一下&#xff0c;发现并没有很多关于这个小型头文件的资料 只好自己上了 int main(int argc, const char * argv[]) {ezOptionParser opt;opt.overview "Demo of parsers features."; //概要说明,一般需要填写&#xff0c;简要介绍程序的作用opt.synta…...

MATLAB:一些杂例

a 2; b 5; x 0:pi/40:pi/2; %增量为pi/40 y b*exp(-a*x).*sin(b*x).*(0.012*x.^4-0.15*x.^30.075*x.^22.5*x); %点乘的意义 z y.^2; %点乘的意义 w(:,1) x; %组成w&#xff0c;第一列为x w(:,2) y; %组成w&#xff0c;第二列为y w(:,3) z; %组成w&#xff0c;第三列为z…...

使用OpenCV实现两张图像融合在一起

简单介绍 图像融合技术是一种结合多个不同来源或不同传感器捕获的同一场景的图像数据&#xff0c;以生成一幅更全面、更高质量的单一图像的过程。这种技术广泛应用于遥感、医学影像分析、计算机视觉等多个领域。常见的图像融合技术包括基于像素级、特征级和决策级的融合方法&a…...

PyTorch学习笔记之基础函数篇(十)

文章目录 6 张量操作6.1 torch.reshape()函数6.1 torch.transpose()函数6.1 torch.permute()函数6.1 torch.unsqueez()函数6.1 torch.squeeze()函数6.1 torch.cat()函数6.1 torch.stack()函数 6 张量操作 6.1 torch.reshape()函数 torch.reshape() 是 PyTorch 中的一个函数&a…...

kubernetes部署集群

kubernetes部署集群 集群部署获取镜像安装docker[集群]阿里仓库下载[集群]集群部署[集群]集群环境配置[集群]关闭系统Swap[集群]安装Kubeadm包[集群]配置启动kubelet[集群]配置master节点[master]配置使用网络插件[master]node加入集群[node]后续检查[master]测试集群 集群部署…...

软件工程师,该偿还一下技术债了

概述 在软件开发领域&#xff0c;有一个特殊的概念——“技术债”&#xff0c;它源于Ward Cunningham的一个比喻&#xff0c;主要用来描述那些为了短期利益而选择的快捷方式、临时解决方案或者未完成的工作&#xff0c;它们会在未来产生额外的技术成本。就像金融债务一样&#…...

HTML5、CSS3面试题(三)

HTML5、CSS3面试题&#xff08;二&#xff09; rem 适配方法如何计算 HTML 跟字号及适配方案&#xff1f;&#xff08;必会&#xff09; 通用方案 1、设置根 font-size&#xff1a;625%&#xff08;或其它自定的值&#xff0c;但换算规则 1rem 不能小于 12px&#xff09; 2…...

pytorch之诗词生成6--eval

先上代码&#xff1a; import tensorflow as tf from dataset import tokenizer import settings import utils# 加载训练好的模型 model tf.keras.models.load_model(r"E:\best_model.h5") # 随机生成一首诗 print(utils.generate_random_poetry(tokenizer, model)…...

Django自定义中间件

自定义中间件 传统方法的的五大钩子函数&#xff1a;&#xff08;需要调用MiddlewareMixin类&#xff09; process_request&#xff0c;请求刚到来&#xff0c;执行视图之前&#xff1b;正序 process_view&#xff0c;路由转发到视图&#xff0c;执行视图之前&#xff1b;正序…...

【JavaScript】JavaScript 运算符 ① ( 运算符分类 | 算术运算符 | 浮点数 的 算术运算 精度问题 )

文章目录 一、JavaScript 运算符1、运算符分类2、算术运算符3、浮点数 的 算术运算 精度问题 一、JavaScript 运算符 1、运算符分类 在 JavaScript 中 , 运算符 又称为 " 操作符 " , 可以实现 赋值 , 比较 > < , 算术运算 -*/ 等功能 , 运算符功能主要分为以下…...

掘根宝典之C++迭代器简介

简介 迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式&#xff0c;使程序员可以对容器中的元素进行逐个访问和操作&#xff0c;而不需要了解容器的内部实现细节。 C标准库里每个容器都定义了迭代器 迭代器的作用类似于指针&#xff0c;可以指向容器中的某个…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...