01.关于C语言指针的理解

1.指针的理解:指针变量用来保存变量的地址

    

 PP这个指针指向P这个变量:写做: int *PP =&P;

p为普通变量,存储值;pp为指针变量,存储地址。


2.指针的类型

int  a= 10; 

int *p =&a;   

double d = 3.14;

double* p2 = &d;

char  c ='d';

char* p3=&c;

指针存储首字节地址 

从首字节开始取4个字节

指针存储首字节地址

从首字节开始取8个字节

指针存储首字节地址

从首字节开始取1个字节

指针的步长为4指针的步长为8指针的步长为1

例:

//指针类型不同,指针的步长就不同(char* 步长为1   int* 步长为4)
int main()
{
    char c = 'a';
    char* p = &c;
    printf("%d\n", p);      //打印首地址:1834227
    printf("%d\n", p + 1);  //打印首地址的下一位:1834228(+1)

    int a = 10;
    int* p1 = &a;
    printf("%d\n", p1);     //打印首地址:1834200
    printf("%d\n", p1 + 1);  //打印首地址的下一位:1834204(+4)
}

int main()
{
	int arr[] = { 1,2,3,4 };
	
	int* p = &(arr[0]);

	printf("%d,%d\n", *p, p);       //     1,4651776
	printf("%d,%d\n", *(p+1), p+1); //     2,4651780
	printf("%d,%d\n", *(p+2), p+2);  //    3,4651784
	printf("%d,%d\n", *(p+3), p+3);  //    4,4651788

}

2.1指针的强制转化

给地址重新赋值(一次性):

int main()
{
	int a = 100;
	int* ip = &a;

	*ip = 0;    //把0一次性赋值到四个字节(解引用)(通过指针间接修改值)
	printf("%d",a);  //输出为0

	
}

给地址重新赋值(分多次):

int main()
{
	int a = 100;
	int* ip = &a;

	char* cp = (char*)ip;  //指针的强制转换
	//逐个字节去赋值
	*cp = 0;
	*(cp + 1) = 0;
	*(cp + 2) = 0;
	*(cp + 3) = 0;
	printf("%d", a);  //输出为0	
}

3.指针的内存大小:指针无论什么类型,统统都是四个字节(32位)

int main()
{
    int a = 10;
    int* p = &a;
    printf("%d", sizeof(int*));   //输出结果为4
}

4.野指针与空指针

野指针(悬挂指针):空间被回收了,指针还指向原来的空间,赋值给了一个不是自己空间的地址

空指针:指针没有指向

int*  return_temp_value() 
{
	int p = 100;
	return &p;
}
int main()
{
	int* p2 = return_temp_value();  //由于局部变量在函数结束时要回收空间,所以此时P2失去指向,为野指针

	p2 = NULL;   //将野指针p2置为空,成为空指针,避免指针指向非法内存

}

5.指针与数组

5.1数组名就是一个指针,但是该指针不能改变指向

int main()
{
	int a = 10;
	int arr[] = { 10,20,30,40 };
	

	printf("%d", *arr); //输出结果为10(数组名就是一个指向首元素的指针)
	
 } 

5.2当数组名作为函数参数的时候,会被编译器转换为指针

void print_array(int arr[]) 
{
	int len = sizeof(arr) / sizeof(arr[0]);  //int*长度为4,int 也为4
	printf("%d", len);
}
int main()
{
	int arr[] = { 10,20,30,40 };
	
	printf("%d\n", sizeof(arr) / sizeof(arr[0]));   //输出为4(16/4)

	print_array(arr); //输出为1(4/4)(当数组名作为函数参数的时候,会被编译器转化为指针)
 }  

6.指针与字符串

6.1对于字符串常量的内存分配

int main()
{
	char s[] = "hello world!";  
	s[0] = 'e';
	printf("%s", s); 

	/*内部操作:编译器先给字符串常量分配内存(只读)
	然后将常量拷贝到字符串数组的内存中(可读可写)
	然后在字符串数组中进行修改,最后输出eello world */
		
 }  

;错误写法:

解释:由于局部变量在函数结束时,内存会回收,因此该空间不能再使用:

 正确写法:

 解释:S这段内存也会在程序结束时销毁,只不过在销毁之前先把地址返回给外部变量,而字符串常量的地址没有被销毁。

6.2编译器会把字符串常量当作地址来处理

int main()
{	
	const char* s1 = "hello world"; //在我们看来是hello world,但是在编译器看来它只是一个字符串的地址
	printf("%s", s1);  //输出hello world

   s1 = "say hi";  //s1可以通过修改地址指向来改变值 
 }  

7.指针数组

7.1普通数组与指针数组的差异

//普通数组:优点:数据独立
 //缺点:无法修改变量的值;在数据较大的情况下,拷贝时间较长
int main()
{	
	int a = 10;
	int b = 20;
	int c = 30;
	int arr[] = { a,b,c };  //拷贝一份数据到数组的地址内
    arr[0]=100;
    printf("%d",a);  //输出为10(改变数组地址中的值而不是a中地址的值)
 } 

//指针数组:数组中存储地址,可以通过数组元素修改外部变量
//优点:由于存储的是地址,拷贝时间大大缩短(每次只拷贝四个字节)
//缺点:数组数据与外部数据关系太紧密(数据独立性太差)

void print_array(int* arr[], int len)
 {
	for (int i = 0; i < len; i++) 
	{
		printf("%d\n", *(arr[i])+1);
	}
}

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int* arr[] = { &a,&b,&c };
	
	print_array(arr, sizeof(arr) / sizeof(arr[0]));   //输出11 21 31
}

7.2 字符串指针数组

int main()
{	
	const char* s = "hello world";    //编译器将字符串自动分配地址去指向"hello world"

	//字符串指针数组:
	const char* names[] = { "Trump","Pelosy","Obama"};   //三个指针,共12个字节

	//遍历输出
	int len = sizeof(names) / sizeof(names[0]);   //  12/4
	for (int i = 0; i < len; i++)
	{
		printf("字符串为:%s" "字符串大小为:%d" "首字符是:%c\n", names[i],strlen(names[i]),*(names[i]));  
		
		//names[i]的类型为char* ,*(names[i])的类型为char
	}
 }  

8.const与指针

8.1  const可以提醒编译器限制变量的更改,但可以通过间接的方法(修改地址)来修改值

ps.const修饰全局变量时,不能通过指针去修改

int main()
{
	const int c = 100;
	int* p = (int*)&c;
	*p = 200;
	printf("%d", c);  //输出结果为200
}

8.2const修饰指针

8.2.1指针常量:指针的地址不可修改,指向的值可以修改

int a = 10, b =20;
int *const p = &a;
 
p = &b; //×
*p = 30; //√

8.2.2常量指针:指针地址可以修改,指向的值不可更改

int a = 10, b =20;
const int* p = &a;
 
p = &b; //√
*p = 30; //×

8.3.3 如果既不能修改指向,也不能修改指向的值

int a = 100;
const int*  const p =&a;

9.指针的使用场景

9.1利用址传递来提高效率

void print_value(int* p) {
	printf("%d", *p);
}
int main()
{
	int a = 100;
	print_value(&a);
}
//编译器对数组的优化:每次传参时将数组名退化成指针,提高传递效率
void printArr(int* arr, int len)   
{
	for (int i = 0; i < len; i++)
	{
		printf("%d\n", arr[i] );
	}
}



int main() 
{
	int arr[] = { 10, 20,30,40 };
	printArr(arr, sizeof(arr)/sizeof(arr[0]));
}

9.2交换俩个变量的值

void swap_value(int* p,int *q)
 {
	
	int temp = *p;
	*p = *q;
	*q = t;
}

int main()
{
	int a = 10;
	int b = 20;

	swap_value(&a, &b);
	printf("a=%d,b=%d", a, b);
}

9.3得到多个返回值(通过指针间接修改变量的方式)


#include <iostream>
#define ARR_LENGTH 10

void get_value(int arr[],int length,int* p_max,int* p_min,int* p_avg)
{
	//求最大值,最小值,以及平均值
	int max = 0;
	int min = 0;
	int avg = 0;
	int sum = 0;
	for (int i = 0; i < length; i++)
	{
		if (arr[i] > max) {
			max = arr[i];
		}
		if (arr[i] < min) {
			min = arr[i];
		}
		sum += arr[i];
	}
	 avg = sum / length;

	//返回三个值
	* p_max = max;
	* p_min = min;
	* p_avg = avg;

}

//打印函数
void print_arr(int arr[],int length) 
{
	for (int i = 0; i < length; i++)
	{
		printf("%d\t", arr[i]);
	}
	printf("\n");
}


int main()
{
	//设置随机数种子
	srand( (unsigned int) time(NULL));

	int arr[ARR_LENGTH] = { 0 };
	//产生十个随机数(1-100)赋值到数组中
	for (int i = 0; i < ARR_LENGTH; i++)
	{
		arr[i] = rand() % 100 +1;
	}

	int max_value = 0;
	int min_value = 0;
	int avg_value = 0;
	print_arr(arr, ARR_LENGTH);

	get_value(arr, ARR_LENGTH, &max_value, &min_value, &avg_value);

	printf("%d\n%d\n%d\n", max_value, min_value, avg_value);
}

10.多级指针的理解

10.1多级指针的定义:

 (1)P左边的星代表它是指针变量,int右边的星代表它指向的数据类型:

例如对int*  *p 的解释:*p表示它是一个指针,int* 表示它指向的是一级指针。

(2)解引用一次就是减少一颗星(*),取地址一次就是增加一颗星(*): 

对P1进行解引用,类型为int,对P2进行解引用,类型为 int* ,依次类推 

PS.由于访问层次的增加,因此尽量减少使用多级指针。

10.2多级指针的应用:
(1)通过二级指针改变一级指针的指向:

int main()
{
	int a = 10;
	int b = 20;

	//修改变量的值:直接修改
	//int* p = &a;
	//p = &b;
	//printf("%d", *p);   //输出20


	//用高一级的指针去间接修改第一级指针的值
	int* p1 = &a;
	int* *P2 = &p1;       //增加“&”要加星
	*P2 = &b;             //解引用要降星

	printf("%d", *p1);   //输出20
}

(2)

void point_to_string(char* s)
{
    s="hello world";
}

int main(void)
{
    char* p =NULL;
    point_to_string(p);
    printf("%s",p);     //输出结果为null,因为"hello world" 赋值给了S并不是P
}

可以用二级指针进行修改:

void point_to_string(char* *s)  //0x666
{
    *s="hello world";
}

int main(void)
{
    char* p =NULL;       //0x666
    point_to_string(&p);
    printf("%s",p);     //输出结果为hello world,通过二级指针修改了一级指针的值
}

(3)

//编写函数,打印字符串指针数组
void print_strings(char* *names, int length){
    for (int i = 0; i<length; i++) {
    printf("%s\n",names[i]);
    }
}
/*数组名是指向数组首元素的指针,数组的names的首元素是char* 类型
数组名是指向char* 指针的指针:char* *
*/

int main(void)
{
    char* names[]={"Trump","Obama","Biden"};
    print_strings(names,3);
}

11.指针练习

(1)指针实现字符串拷贝

//拷贝字符串
void copy_string(char* src, char* dst) 
{
	if (src == NULL){
		printf("src指针为空\n");
		return;
	}
	if (dst == NULL) {
		printf("dst指针为空\n");
		return;
	}

	char* s = src;       //用新变量保存指针,原来的指针保持原样

	while (*s !='\0')    //字符的最后一位都为\0
	{
		*dst = *s;       //char*类型每次向后移动一位
		++s;
		++dst;
	}

	*dst = *s;   //将字符串最后的\0拷贝到目标空间
}

int main()
{
	 char* src = "hello world!";   //源字符串
	char dst[128] = { 0 };              //目标字符串
	copy_string(src, dst);
	printf("%s", dst);
	
}

(2)指针实现字符串拼接

//把原字符串拼接到目标字符串之后
void str_cat(char* src, char* dst) {
	if (src == NULL) { return; }
	if (dst == NULL) { return; }
	char* p = src;

	while (*p != '\0') //先找到目标字符串\0的位置
	{
		++p;
	}

	while (*dst != '\0') { //将src从\0之后开始赋值
       *p=*dst;
		++p;
		++dst;
	}
	*p = '\0';	  //末尾补0
}


int main()
{
	char s1[128] = "hello";   //源字符串"hello\0"
	char s2[] = "world";      //目标字符串"world\0"
	 str_cat(s1, s2);
	 printf("%s\n", s1);

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值