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

遵义市做网站公司郑州网站推广公司

遵义市做网站公司,郑州网站推广公司,网站设计公司列表,外贸线上推广字符串 概念 定义:使用空字符结尾的一维字符数组空字符/结束符/ NUL:ASCII 码表中值为 0 的控制字符,使用 \0 表示,用于标记字符串的结束 声明 字符数组声明:有效字符数等于字符数组长度减一 int main() {char s…

字符串

概念

  • 定义:使用空字符结尾的一维字符数组
  • 空字符/结束符/ NULASCII 码表中值为 0 的控制字符,使用 \0 表示,用于标记字符串的结束
    在这里插入图片描述

声明

  • 字符数组声明:有效字符数等于字符数组长度减一
int main()
{char site[10];          // 定义一个能存储10个字符的变量,其中最后一个字符用于默认为空字符,故实际只能存储9个字符,有效字符数为9
}
  • 字符指针声明:
char *str;

初始化

  • 字符串字面量:被双引号包裹的字符系列,如 "aaa" 长度为 3
// 不要将字面量/输入的字符串直接赋值给字符指针;要么给字符指针开辟空间,然后将字面量复制过去;要么直接赋值给字符数组
  • 字符数组初始化:
int main()
{char site[10];               // 定义一个能存储10个字符的变量,其中最后一个字符用于默认为空字符,故实际只能存储9个字符,有效字符数为9char site[10] = {0};         // equal char site[10] = "0"; 将数组能存储的有效字符都设置为字符0char site1[10] = "RUNOOB";   // 若指定了数组长度,则赋值时字面量字符数必须小于数组长度,不然 sizeof 求出的长度是一个不确定值char site3[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};    // 一定要加空字符,不然 strlen(site3) 有问题,不过数据长度计算没问题char site2[] = "RUNOOB";     // 会自动计算字面量的字符数,然后将其加一作为字符数组的长度char site6[] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};     // 会自动计算字面量的字符数,然后将其加一作为字符数组的长度// 不能在定义数组的时候使用变量来定义数组长度,因为字符数组创建之后,其长度不能被修改,而变量是可以修改的,所以只能用常量int NUM1 = 101; char str1[NUM] =  "hello"; // ERROR const int NUM2 = 101char str2[NUM2] =  "hello"; // RIGHT  
}

  • 字符指针初始化:
int main()
{char *str = NULL;// 只有将字符串字面量赋值给字符指针时,此字符串才会存储在静态存储区,存储在静态存储区的字符串是不被允许修改的char *strPointer = "test";   // 不要这么做// 开辟空间并复制char *site2 = malloc(strlen("RUNOOB") + 1);strcpy_s(site2, strlen("RUNOOB") + 1, "RUNOOB");
}

修改

  • 字符数组:已经赋值过的字符数组不能像 Java 一样直接使用赋值运算符将新字符串赋值给字符数组,得使用复制函数;不过可以直接修改字符数组中元素
char oldValue[20] = "hello";
char newValue[20] = "hello world";
strcpy(oldValue, newValue); // right; note:  newValue lenght < oldValue lenght
printf("%s\n", oldValue);oldValue = "test"; // error
  • 字符指针:可以直接赋值,也可以直接修改所指向的字符串中的字符,但前提不是使用字符串字面量直接赋值给字符指针
char str1[20] = "hello world";char* str = str1;
str = "hello!!!";
printf("str value: %s\r\n",str);

计算

strlen

  • 作用:计算字符串 str 有效字符数;本质上就是遍历字符串直到遇见空字符结束 --- 空字符不参与计数;若没有空结束字符,就会出现问题
  • 原型:strlen( const char *str )
char str[] = "abc";
int len = strlen(str);
printf("%d\n",len); // 打印3char str[5] = "abcde";
int len = strlen(str);
printf("%d\n",len); // 打印5

sizeof

  • sizeof :返回一个变量/类型所占的内存字节数
int a = sizeof("1") // a = 2
char str[] = "A";
int num = sizeof(str);
printf("%d",num); // num == 2, 因为创建字符数组时,未指定长度,故计算时会把结束符'\0'算进去char str[] = {'a','b'};
int num = sizeof(str);
printf("%d",num); // num == 2, 因为使用花括号创建字符数组时,未指定长度,故计算时计算花括号中的字符数,若花括号中有空字符,则也会计算进去char str[5] = "abc";
int len = sizeof(str);
printf("%d",len); // len == 5, 因为创建字符数组时指定了长度为5

打印

  • 打印:打印字符串不会显示空字符 \0
char str1[] = "hello world";
printf("%s",str1); // 打印hello world,而不是hello world\0char *str2 = "hello world";
printf("%s\n",str2); // 打印hello world,而不是hello world\0

做参

  • 字符串打印:不要将字符串字面量/输入字符串直接赋值给字符指针,然后传参
void test(char *str)
{printf("%s\n",str); // 正确打印
}int main()
{char  str2[] = "hello world";test(str2);
}
  • 字符串修改:不要将字符串字面量/输入字符串直接赋值给字符指针,然后传参
void updateChar(char *str)
{str[0] = 'a';printf("%s\n",str); // 正确打印
}int main()
{char  str2[] = "hello world";  // 不能用字符指针接受字符串字面量,必须使用字符数组updateChar(str2);printf("%s\n",str2);          // hello world
}
  • 修改变量指向的字符串:若要将传入的指针指向指向新字符串,必须使用 char * 类型指针的地址作为函数实参,使用二级指针作为函数形参因为字符数组一经初始化就不能修改
void updateAllError(char *str)
{str = "hello";              // 让str指向了新字符串所在的地址,即修改了str的值printf("%s\n",str);         // 正确打印
}void updateAllRight(char **str)
{*str = "hello\0";              // 修改了传入的存储字符指针变量的地址中的值,即修改了入参的值,让其指向新变量printf("%s\n",*str);           // 正确打印
}int main()
{char  str1[20] ;scanf_s("%s",str,sizeof(str));char *str2 = str1;           // 必须,不然会出错updateAllError(str2);        // helloprintf("%s\n",str2);         // hello worldupdateAllRight(&str2);       // helloprintf("%s\n",str2);         // hello
}

数组

  • 初始化:若使用花括号初始化,花括号中的值会对应赋值给数组,数组剩下的元素默认用 0 初始化;字符数组/指针数组也是类似的
int main(void) {int arr[5];           // random valueint arr1[5] = {0};    // 0 0 0 0 0  int arr2[5] = {1};    // 1 0 0 0 0int arr3[5] = {1,1};  // 1 1 0 0 0for(int i = 0; i < 5 ;i ++) {printf("arr[%d]: %d\n",i,arr[i]);}return 0;
}
  • 补充:数组名其实就是一个指针,如下打印 site2site4 显示的值是一样的
// 打印指针用p
printf("%p\n",site4);// 0000000898dff752
printf("%p\n",site2);// 0000000898dff752

指针

  • 指针初始化:不知道用啥初始化就用 NULL
  • 数组与指针:数组变量本质上就是指针常量,数组变量一经赋值,其值就是数组的首地址,其值不允许被修改
char str[10] = "hello"`
str = "world";  // error, 数组变量str的值不允许被修改
  • 常量指针:指针变量向地址中的值不允许修改,const 写在类型前:const int *p
  • 指针常量:指针变量的值不允许修改,const 写在类型后:int const *P
  • 指针数组:变量名表示一个数组,该数组中的每一个元素都是指针:int *p1[3]p1 是一个指针数组,数组元素为 int * 类型/ int 型指针
  • 数组指针:变量名表示一个指针,该指针指向一个数组:int (*p)[10]p 是一个数组指针,指向一个 int 型数组,该数组有10个元素
  • 结构体指针:变量名表示一个指针,该指针指向一个结构体
typedef struct{int age;char name[16];
}Person;int main(){Person liu = {22,"liu"};Person *li = malloc(sizeof(Person));// li = &liu;
}
  • 函数指针:变量名表示一个指针,该指针指向一个函数

    • 定义格式:int (*p)(int x, int y)
    • 语法说明:定义一个名为 p 的函数指针,该指针可以指向具有该种模式的函数,模式:有两个参数,参数类型均为 int ,返回值类型为 int
    • 使用案例:
// 定义函数
int maxValue (int a, int b) {return a > b ? a : b;
}  
// 定义函数指针,指向的函数模式与maxValue函数模式一致
int (*p)(int, int) = NULL; 
// 指向函数 
p = maxValue;
// 指针调用
p(20, 45);

内存知识

内存空间布局

  • 程序与进程:C代码经过编译后就生成了存储在磁盘上的程序,运行程序就产生了进程
  • 程序组成:通常都是由 BSS 段、data 段、text 段三个组成的

注意:如果用 0 初始化全局/静态变量,等于没有初始化,因为默认为 0

在这里插入图片描述

函数栈与变量

  • 栈与函数:C 程序始于 main函数,当执行 C 程序时,会先执行 main 函数,此时操作系统会为 C 程序分配一个函数调用栈,同时将 main 函数的函数调用类型对象入栈;若 main 函数中调用了函数 A ,则会将 A 函数的函数调用类型对象压入栈中;若函数 A 执行完毕,则会将 A 函数的函数调用类型对象出栈;如此,执行一个 C 程序,会得到一个函数调用栈,栈中每个元素都表示一个函数调用类型对象,该对象存储了该函数中定义的变量,以及函数签名中的参数

参考文章:C语言内存模型-CSDN博客

  • 栈与变量:函数中定义的变量存储在函数调用类型对象中;当函数被调用时,会为其创建一个函数调用类型对象,并将其压入函数调用栈;当函数执行完毕,会将该对象出栈,该对象的入栈时间和出栈时间差就是函数中定义的变量的生命周期

重点:对于函数中创建的指针/数组变量 A ,在函数调用结束后,其生命周期就结束了,存储变量的地址被回收,其指向的内存地址也会被操作系统回收,变成系统内存,这段内存不允许再被程序访问,除非重新分配给程序;此时若再定义指针/数组 B则有一定概率将之前 A所指向的内存地址分配给 B错误案例如下所示

void func1(char *str, char *data[])
{char comBufTmp[256] = {0};char* test = str;memcpy(comBufTmp, str, 256);char delim[] = " ";int index = 0;data[index] = strtok(test, delim);while (data[index] != NULL) {index++;data[index] = strtok(NULL, delim);}
}void func2()
{char stack[2048];memset(stack, 0, 2048);
}int main()
{char str[] = "test test test test test test test test test test";char *data[10];int data1[10];int data2[10];func1(str,data);for (int i =0; i < 10; i++) {data1[i] = *(int *)(data[i]);}func2(); // 会清掉comuffTmp所指向内存的值for (int i =0; i < 10; i++) {data2[i] = *(int *)(data[i]);}for (int i =0; i < 10; i++) {printf("data1[%d]=%d, data2[%d]=%d\n", i, data1[i], i, data2[i]); // data1 和 data2 不一样}}
char* func1()
{char str[256] = {0};return str;
}int main()
{char* str = func1(str); // ERROR
}

Glibc 内存管理

  • Glibc 内存管理:Linux 下的内存管理程序,C 语言的 malloc()/free()Glibc 封装提供的 APIAPI 实现中采用内存池来优化性能,并避免了直接与操作系统交互

  • 申请内存原理:

    • malloc() 申请内存时,Glibc 会判断内存池中有空闲内存:
      • 若有:则返回内存池中的内存块
      • 若无:通过 brk/mmap 系统调用去操作系统申请,释放时也先释放到内存池中以备下次使用
  • Glibc 返回给用户的内存块内部用 Chunk 对象管理,它给用户使用的有效内存块前后加了一些控制信息,来记录分配信息,例如内存大小、与其它内存块的连接,以方便完成分配和释放工作;Chunk 对象用分箱式内存管理方式,分配时从空闲并合适大小的链表中获取
    在这里插入图片描述

常见内存操作

内存申请

  • 申请内存大小问题
    • malloc 参数是无符号类型,传入参数 <0 会转换成大整数
    • malloc 参数为 0 时,可能返回非空值,影响后续程序判断
    • malloc 时如果使用外部参数,一定要判断参数有效性

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

  • 没有判断 malloc 返回值

    • malloc 可能失败,失败时返回 NULL ,不判断返回值可能导致引用 NULL 指针问题
    • malloc 一定要判断返回值是否为 NULL ,并对返回 NULL 的场景设计完善的错误处理
  • 在中断上下文中使用 malloc

    • malloc 过程可能 sleep ,等待别的线程完成内存回收
    • 中断上下文不能被抢占,回收线程可能得不到执行,导致死锁
    • 避免在中断中使用 malloc

内存引用

  • 指针引用问题

    • 引用 NULL 指针,程序崩溃
    • 引用未初始化指针,可以指向任意地址,可能崩溃或数据错误
    • 引用已释放指针,该地址可能已经被其他变量使用,导致数据覆盖
    • 指针初始化、释放后都置为 NULL,引用时检查指针的有效性
  • 指针偏移问题

    • 指针偏移超出了分配空间,可能引用其他变量地址,导致数据错误
    • 指针类型与分配时不一致,偏移量计算会出错,导致数据错误
    • 注意数组下标(特别是字符串结束符),谨慎使用指针类型强转
    • 使用安全函数,并正确填写 buffer_size 参数
    • 仅进行地址偏移计算,不进行内存引用,不会发生问题,如计算 struct 成员偏移量

内存释放

  • 动态申请的内存未释放

    • 申请的内存不释放会导致内存泄漏,相应内存空间一直被占用
    • 内存申请指针必须释放、返回或保存,否则一定有内存泄漏
    • 通过函数返回状态要能判断内存状态,避免使用 realloc
  • free 非动态申请的指针

    • free 只能处理堆空间,全局变量地址和栈地址不能使用 free 释放
    • 不能对偏移过的指针进行 free,可能导致内存泄漏
    • 区分栈指针和堆指针,尽量不要对内存申请指针进行偏移操作
  • 重复释放同一地址

    • 释放后的地址可能已经被其他变量使用,再次释放可能会导致其他变量的数据被覆盖
    • free 后将指针变量置为 NULL ,避免重复释放

并发访问

  • 申请后使用,使用完释放,不能重复释放

  • 共享地址大小、类型要统一

    • 向其他线程共享栈空间地址
    • 函数或线程退出时栈变量销毁,其他线程不能再访问共享变量
    • 函数或线程退出前需要等待其他线程使用完毕
    • 通过引用计数确认其他线程是否还在使用
  • 与其他线程共享堆空间地址

    • free 释放后所有线程不能再访问共享变量
    • 最后一个使用者通过 free 释放共享地址
    • 通过引用计数确认最后一个使用者

循环读

数字循环读

int a;
while(scanf("%d",&a) != EOF){printf("%d\n",a);
}

字符循环读

char a;
while(scanf("%c",&a) != EOF){printf("%c\n",a);
}
while((a = getchar()) != EOF){printf("%c\n",a);
}

字符串循环读

const int NUM = 101;
char str[NUM];
while (scanf_s("%s", str, sizeof(str)) != EOF) {printf("%s\n",str);
}
while ((int)gets(str) != EOF) {puts(str);
}
http://www.dtcms.com/wzjs/76821.html

相关文章:

  • python开发app河南seo优化
  • 网站建设推介公司宣传软文
  • 行业b2b网站建设百度推广一年要多少钱
  • mac上用什么做网站ip域名查询地址
  • 用wordpress搭建网站北京昨天出啥大事了
  • wordpress主题 时间轴网站seo优化公司
  • 南昌seo关键词廊坊首页霸屏优化
  • 电子商务网站开发过程论文6百度搜索引擎优化指南最新版
  • 思明建设局网站百度seo优化多少钱
  • app小程序网站开发百度seo新算法
  • p2p网站制作百度推广代运营
  • 广州网站建设公司乐云seo598培训后的收获和感想
  • 有那个网站可以做免费的投票长春百度推广公司
  • 西安到北京飞机票价网站seo诊断技巧
  • 做设计哪个网站可以接单不死鸟分享友情链接
  • 如何在网站后台备份数据库表企业网站制作价格
  • 做结构图的网站广告推广方式有哪几种
  • 日照网站开发中国十大关键词
  • 顶呱呱网站建设信息流优化师是什么
  • 中英文网站建设的差别佛山网站开发公司
  • 最新网站开发需求文档上海seo优化
  • 创业服务网网站建设方案项目书百度入口网页版
  • 老外做的汉字网站天门seo
  • 自己做网站用中文为什么是乱码必应搜索引擎首页
  • 制作公司网站一般多久能好热门推广平台
  • 可以做直播卖产品的网站咨询网络服务商
  • 北京代做网站北京网站建设公司报价
  • 张家港做网站收费标准热搜榜上能否吃自热火锅
  • jsp新闻网站建设站点宁波seo怎么做引流推广
  • 网站建设 图片上传做企业推广