一、const(关键字)修饰指针
1.const修饰变量
变量是可以改变的,把地址给一个指针变量来保存时,也可以通过指针来修改该变量
#include <stdio.h>
int main()
{
int m = 0;
m = 20; //m是可以修改的
const int n = 0;
n = 20 ; //n是不能被修改的,报错
return 0;
}
当这个const放在int后面,变量前面时

“表达式必须是可修改的左值”意思是:
例如,5 = 80;这个代码明显是错的,5不是变量,不能用来存80
a被const修饰后,在检查时会认为a是不能被修改的。一般都是这么写:
const int a = 10;
#include <stdio.h>
int main()
{
const int n = 0;
printf("n = %d\n", n);//输出n=0
int* p = &n;
*p = 20;
printf("n = %d\n", n);//输出n=20
return 0;
}
那么,能不能也不让通过指针来修改变量a当中的内容呢
2.const修饰指针变量

const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容(即指针保存的地址)可变。const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
(指针指向的内容---*p 指针变量本身的内容---p)
二、野指针
1.野指针成因
a.指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20; //非法访问内存
return 0;
}
b.指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
c.指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n; //n在函数结束时销毁,返回的地址是无效的
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}

2.规避野指针的方式
a.指针初始化

b.小心指针越界
c.指针不再使用时,置NULL,使用时检查有效性
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
for(i = 0; i < 10; i++)
{
*(p++) = i;
}
//此时p已经越界了,可以把p置为NULL
p = NULL;
//下次使用的时候,判断p不为NULL的时候再使用
//...
p = &arr[0];//重新让p获得地址
if(p != NULL) //判断
{
//...
}
return 0;
}
d.避免返回局部变量的地址
如不要返回函数内部变量的地址
三、assert断言
1.assert.h
2.用assert来检查某指针是否是NULL
#include<stdio.h>
#include<assert.h>
void func(int* pc)
{
assert(pc != NULL);
*pc = 10;
}
int main()
{
int* p = NULL;
func(p);
return 0;
}

3.用assert检查
定义函数时,如果参数是指针,都要进行参数的合法性判断
void func(int* pc)
{
assert(pc != NULL);
if (pc == NULL)
{
printf("pc = NULL\n");
return;
}
//参数的合法性判断
*pc = 10;
}
int main()
{
int* p = NULL;
func(p);
return 0;
}
在以上代码中,如果在debug版本下,程序会因为assert断言失败报错;在release版本会打出“pc==NULL”,这样就实现了双重检查,且assert报错会精准给出位置
四、指针的使用和传址调用
1.strlen的模拟实现
strlen是C语言中的一个库函数,要使用该函数,要引入头文件<string.h>

size_t 可以理解为unsigned int
字符串——“”引起的若干个字符:“hello” “a” (本质是字符数组,结尾自动带\0)
字符——‘’引起的单个字符:‘a’ ‘!’ ‘\n’
字符串的长度——字符的个数:如“hello”是5
怎么理解这个参数const char* str?
char* str = “hello”; 之所以合法,是因为C语言字符串“hello”本质上是一个地址,而不是一个字符串整体。str这个指针存的是hello的起始地址(即字符h的地址)。
如果写成:
char str[ ] = "hello";
才是把字符串的每个字符拷贝到数组里。

“hello”是字符串常量,这种情况是无法被修改的。需要提醒的是,%s是用来打印字符串的,它需要接收一个char*类型的地址(字符串的首地址)
要让字符串可以被修改,必须把它放在栈上的字符数组中:
#include <stdio.h>
int main()
{
char str[] = "hello"; // 字符串被复制到可修改的栈数组中
str[0] = 'a'; // 合法修改数组元素
printf("%s\n", str); // 输出 aello
return 0;
}
用一个函数模拟strlen:
int my_strlen(const char* str)
{
int count = 0;
assert(str != NULL);
if (str == NULL)
{
return 0;
}
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
定义函数时,如果参数是指针,都要进行参数的合法性判断
2..传值调用和传址调用
传值调用:实参的值传递给形参,此时形参的改变并不会影响实参的值
void swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
传址调用:将变量的地址传递给函数,在函数内部通过指针来操作变量
void swap(int* px, int* py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}

3964

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



