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

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修饰指针,可以有下面三种结构:

  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型数据的数组。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值