C语言知识-零零散散(四)

手动实现strcmp函数

strcmp函数是用于比较两个字符串的大小,如果相等返回0,如果第一个字符串比较大返回1,否则返回-1。

int mystrcmp(const char* str1, const char* str2){// 如果发现其中一个字符等于'\0',或者两个字符不相等,结束循环while(*str1  && *str2 && (*str1 == *str2)){ str1++;  // 地址自加,指向下一个字符str2++;}if(*str1 == *str2){   // 如果两个指针都是指向'\0',表示相等,返回0return 0;}// 比较两个字符串不相等字符的大小return (*str1-*str2)>0 ? 1 : -1;
}

指针的大小

指针存储的是一个内存地址,地址是一种它是从零开始向上递增,根据操作系统的不同,32位操作系统中,sizeof指针的大小为4字节,64位操作系统为8字节(与指针的类型无关,char* int* float*等类型无关)。

野指针

野指针的概念

野指针,指向内存被释放的内存或者没有访问权限的内存的指针。

野指针产生的原因

  • 指针没有初始化。**解决办法:**指针声明时初始化,可以是具体的地址值,也可让它指向NULL。
  • 指针被free或者delete后,没有置为NULL。**解决办法:**指针指向的内存空间被释放后指针应该指向NULL。
  • 指针操作超越了变量的作用范围。**解决办法:**在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。

空指针

在c语言中,如果一个指针不指向任何对象,这种指针被称为空指针。空指针的值一般为NULL。NULL是一个宏定义,在stdio.h中被定义为:

#define NULL    ((void *)0)

需要注意的是,不要企图使用空指针执行内存中存储的内容,这是不合法的。定义一个空指针:

int* ptr = NULL;

不同的编译器,对内存位置为0的地方采取的保护方式不同。某些实现对内存位置0加了硬件级的读保护;有些实现只允许读不允许写;有些既允许读又允许写。所以,下面这段代码根据实现的特性,可以读内存位置0的机器上可以运行,其他不可以。

#include <stdio.h>int main(){char *p;p = NULL;printf("%d\n", *p);return 0;
}

在vs上运行,是拒绝访问的

void* 指针

void* 指针可以指向任意变量的内存空间,所以也可以称为万能指针,可以传递任意类型的指针,而不引起“类型不匹配”相关的编译警告。举一个简答的例子,使用万能指针改变一个变量的值,

#include <stdio.h>
#include <stdlib.h>int main(){void* p = NULL;   // 定义void* 指针int a = 1;p = (void*)&a;    // 给指针p赋值*((int*)p) = 10;  // 这里p是void*类型,需要强制转化为(int*)printf("%d\n", a); // 打印输出system("pause");return 0;
}

void * 指针还可以将不希望被公开的数据结构隐藏,避免外界调用“意外”或者“恶意”的修改。

void *p = init();
handle(p);

如上面代码,如果我们想去修改p指向的数据结构,就要了解init()的返回内容。外部开发者,在调用init()函数时,就没有办法去改变p指向的内容,使得整个的代码更加的安全、稳定。

const修饰常量是个假常量?

const关键字,用来定义一个只读的变量或对象,可用于保护某些变量的内容在使用中不会被更改。但是在c语言中const修饰的常量是一个假的常量,可以通过指针进行修改。例子如下:

#include <stdio.h>int main(){const int a = 100;int* p = (int*)&a;*p = 99;printf("%d\n", a);return 0;
}

const修饰指针

const修饰指针,可以有下面三种结构:

  1. const 数据类型* 变量名 (或者 数据类型 const * 变量名 这两种方式是等价的)
  2. 数据类型* const 变量名
  3. const 数据类型* const 变量名

最后一种比较好的区别,通常1和2两种情况比较容易混淆。可以通过下面的方法进行区分,在定义一个指针的时候:数据类型 * 变量名, * 后面的变量名表示一个指针, * 前面的数据类型表示指针要指向的数据结构。所以我们以 * 分界,如果const在 * 前面,表示修饰的是数据类型,表面指针指向的内容是不可修改的(也就是常量指针),如果const在 * 后面,表示修饰的是指针,表示指针地址是不可以被修改的(也就是指针常量)。如果 * 前后都有const修饰,表示指针的地址和指针指向的内容都是不可以被修改的。举例如下:

#include <stdio.h>int main(){int a= 10;int b = 20;const int* p1 = &a;int* const p2 = &a;const int* const p3 = &a;p1 = &b;  // p1的地址可以被修改// *p1 = 30; // 错误,p1指向的内容不可以被修改*p2 = 100; // p2指向的内容可以被修改// p2 = &b; // p2的本身不可以被修改printf("%d\n", *p1);printf("%d\n", *p2);printf("%d\n", *p3);return 0;
}

下面引出来两个问题。

下述4个指针有什么区别?

  1. char * const p1;
  2. char const * p2;
  3. const char * p3;
  4. const char * const p4;

答案是:

p1是指针常量,它本身不能被修改,指向的内容可以被修改。
p2和p3是常量指针,它本身可以被修改,指向的内容不可以被修改。
p4本身是常量,并且它指向的内容也不可被修改。

指针常量与常量指针的区别

指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。
指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。

数组名和指针之间的区别

  1. 在分配内存方面,数组一般在栈区开辟内存(全局和静态数组在静态区),内存的回收是由编译器或者操作系统完成,不会出现内存泄漏;指针变量一般是在栈上开辟内存,如果是开辟动态内存空间,就需要使用malloc或者new开辟,指针变量的值保存的是开辟内存空间的首地址,使用完毕后采用free或者delete释放,避免内存泄漏。

  2. 指针和数组都可以初始化一个字符串,指针初始化字符串时 char* p="hello";,指针变量存储在栈区,"hello"存在静态区,所以对应的字符无法修改;但是使用数组初始化字符串时,char arr[] = "hello";时,它是在栈上分配内存,所以可以通过数组下标对成员进行修改,如 arr[0]=H;

  3. sizeof计算大小,使用sizeof一个数组时,计算的是数组的容量;sizeof一个指针时,就是一个指针的大小(一般32位操作系统是4个字节大小)。

  4. 数组直接的赋值不能直接使用赋值符号,应采用数组下标逐个的赋值,如下代码是错误的

    char arr[] = "helo";
    char arrcpy[10];
    arrcpy = arr;   // 错误
    

    对于数组中的内容赋值给指针,也不能直接采用赋值的形式,应该先采用malloc申请一块内存,然后再进行内容的拷贝

    char arr[] = "hello";
    char* p;
    // p=arr //该种方式只是把arr的首地址赋值给指针p,并不是内容的拷贝
    p = (char*)malloc(sizeof(arr));
    strcpy(p, arr);
    
  5. 当数组作为函数参数传递时,该数组退化为同类型的指针。

看如下代码,输出结果为?

int arr[] = {1,2,3,4,5,6,7,8};
int* p =arr;
p++;
printf("%d\n", p[3]);

上述代码输出结果为5,数组名赋值给指针p,这时p[0]=arr[0],执行p++后,p[0] = arr[1],所以p[3] = 5。

手动实现strchr函数

char* mystrchr(const char* str, const char chr){const char* p = str;while(*p != '\0'){if(*p == chr){return (char*)p;}p++;}return NULL;
}

函数strchr,查找字符串str中首次出现chr的位置。

实现函数的时候,定义str和chr都是使用const修饰,保证是不可修改的。在函数内定义一个临时指针,通过不断的移动指针的位置和chr做匹配,如果找到了匹配的字符,返回地址,否则返回NULL。

数组指针和指针数组

数组指针就是指向数组的指针;指针数组就是一个数组,但是数组中存放的是指针。

int arr[] = {1,2,3,4,5,6};
int* p1 = arr; // 定义一个指针指向一个数组,这个指针就是数组指针
int* p2[6];  // 定义一个指针数组

那么这样一个问题

int *p1[6];
int (*p2)[6];

哪个是数组指针?哪个是指针数组?

答案是p1是指针数组,p2是数组指针。区分的时候,可以按照符号的优先级进行区分,其中 ()>[]>*。变量p1,首先是 p1[6]这个数组,数组里面存放的都是指针,每个指针指向一个int型的变量。变量p2,先运算()里面的,所以p2是一个指针,他指向的是一个包含6个int型数据的数组。

C语言知识-零零散散(四)相关推荐

  1. C语言知识-零零散散(三)

    C语言知识-零零散散(三) while循环中的scanf函数 首先给出下面的源码: #include<stdio.h> #include<stdlib.h>int main() ...

  2. C语言知识-零零散散(一)

    C语言知识-零零散散(一) 分类记忆C语言32个关键字 分类记忆 C语言编译过程 预处理,主要处理源代码中的预处理指令,引入头文件,去除注释,处理所有的条件编译指令,宏替换,添加行号.经过预处理指令后 ...

  3. C语言知识体系思维导图

    C语言是基础,如果想要更好的发展,基础还是要打扎实,需要积累.不积跬步无以至千里.所以为了读者们的架构师之路(不想成为架构师的程序员不是合格程序员),本人讲C语言的知识整理下,给大家建立一个知识体系. ...

  4. c语言编程定位的计算机,高校计算机专业C语言教学的四个定位

    高校计算机专业C语言教学的四个定位 来源:用户上传 作者: 姜晓峰 摘 要:本文所讨论的是高校计算机专业C语言教学的课程性质.教学目标.教学内容.教学理念四个方面的定位问题.作者从教学的实际情况,目前 ...

  5. 知物由学 | 再造巴别塔,我们如何进行NLP跨语言知识迁移?

    知物由学 | 再造巴别塔,我们如何进行NLP跨语言知识迁移? 自以 BERT 为代表的预训练语言模型诞生起,关于其跨语言版本的探索研究就从未停止过.2020 年 4 月,Google 发布了 XTRE ...

  6. 【自然语言处理】【知识图谱】MTransE:用于交叉知识对齐的多语言知识图谱嵌入

    MTransE:用于交叉知识对齐的多语言知识图谱嵌入 <Multilingual Knowledge Graph Embeddings for Cross-lingual Knowledge A ...

  7. C语言 操作系统实验 四种调度(最高响应比优先算法 HRN)

    注: 本文是四个调度算法的第一篇算法. 本文是根据CSDN上某一FCFS调度算法魔改来的,所以FCFS的算法不会发到网站. 我是个菜鸡,发文是为了纪念自己完成了代码,以及累计自己的经验. 如有知识错误 ...

  8. [Java面试九]脚本语言知识总结.

    [Java面试九]脚本语言知识总结. 核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选 ...

  9. c程序语言第四版实验报告,C语言程序设计第四次实验报告

    C语言程序设计第四次实验报告 姓名:熊毅 实验地点:家 实验时间:2020.04.09 实验项目: 5.3.1练习2 求数列的前n项和 5.3.2练习2 求水仙花数 5.3.4 十进制转换 5.3.5 ...

最新文章

  1. R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(双分类变量分组:色彩配置、添加箱图、位置参数调整)实战
  2. OSS.Common扩展.Net Standard支持实例分享
  3. Linux字符设备驱动剖析
  4. Linux内核:网络过滤器简介与示例代码
  5. __attribute__((visibility()))
  6. 执行环境,作用域链,闭包
  7. 【PAT乙】1069 微博转发抽奖 (20分) set
  8. hadoop安装教程学习
  9. java 停止定时器_实例助解java定时器设置及停止的方法
  10. Qt 批量替换指定文本为目标文本
  11. 这三款曾红极一时的软件,现已风光不再,而遭嫌弃的它成了香饽饽
  12. Iphone备份SHSH的方法步骤
  13. 高可用和热备份是什么意思?
  14. linux mint(ubuntu)频率锁定解决
  15. 西瓜测试软件,西瓜视频v2.0.0
  16. ISCC 2021 SSTI
  17. 值得收藏-装修攻略全
  18. 第十一天: SD卡原理分析及SD卡启动详解
  19. Kafka 消息中间件
  20. Android Google Services Framework Google Play

热门文章

  1. Ajax + $ajax
  2. 【密码学基础】03 传统加密技术
  3. 终极单词index 排序 G-H
  4. Javascript中append和appendChild有什么不同?
  5. Linux系统下的文件传输
  6. js控制网页动态效果
  7. 数据库索引——唯一索引、主键索引、聚集索引
  8. 自定义控件其实很简单 四
  9. CMA认证与CNAS认可的八大区别,你知道几个?
  10. 小程序之任务发布与接单平台