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',表示相等,返回0
return 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修饰指针,可以有下面三种结构:
- const 数据类型* 变量名 (或者 数据类型 const * 变量名 这两种方式是等价的)
- 数据类型* const 变量名
- 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个指针有什么区别?
- char * const p1;
- char const * p2;
- const char * p3;
- const char * const p4;
答案是:
p1是指针常量,它本身不能被修改,指向的内容可以被修改。
p2和p3是常量指针,它本身可以被修改,指向的内容不可以被修改。
p4本身是常量,并且它指向的内容也不可被修改。
指针常量与常量指针的区别
指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。常量指针是指定义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。
指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。
数组名和指针之间的区别
-
在分配内存方面,数组一般在栈区开辟内存(全局和静态数组在静态区),内存的回收是由编译器或者操作系统完成,不会出现内存泄漏;指针变量一般是在栈上开辟内存,如果是开辟动态内存空间,就需要使用malloc或者new开辟,指针变量的值保存的是开辟内存空间的首地址,使用完毕后采用free或者delete释放,避免内存泄漏。
-
指针和数组都可以初始化一个字符串,指针初始化字符串时
char* p="hello";,指针变量存储在栈区,"hello"存在静态区,所以对应的字符无法修改;但是使用数组初始化字符串时,char arr[] = "hello";时,它是在栈上分配内存,所以可以通过数组下标对成员进行修改,如arr[0]=H;。 -
sizeof计算大小,使用sizeof一个数组时,计算的是数组的容量;sizeof一个指针时,就是一个指针的大小(一般32位操作系统是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); -
当数组作为函数参数传递时,该数组退化为同类型的指针。
看如下代码,输出结果为?
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型数据的数组。
3664

被折叠的 条评论
为什么被折叠?



