1.assert断言
这里先说一下assert断言是什么:assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件,如果不符合就报错终止运行。这个宏常常被称为“断言”。
那简单来说assert其实就是用来中断不符合条件的情况。
assert(p != NULL);
就像上述代码一样,如果p !=NULL就会继续往下进行,如果p=NULL程序就会终止,并且给出报错提示,并且还会给出错在哪个位置。是不是特别好用。如果不能理解没关系这里给串代码。
因为这里的p = &a不等于NULL这里就是对的能继续执行下去,那下面再给个等于NULL的代码
到这里就报错了,而且还给了你错误的地方,多好 。如果己经确认程序没有问题,就不用再做断言了,就在#include<assert.h>前面定义一个宏NDEBUG写法是这样#define NDEBUG。
但是这个assert也是有缺点的就是引入了额外的检查,增加了程序运行时间。所以
学习了assert我们要学着去优化一下之前的代码,比如之前的strlen求字符串长度的代码,优化如下 :
对比这两张图片我们可以看出来第一点就是加了assert 断言,使代码的安全性更进了一步。假如说,有人不小心把arr改成NULL那么传给s的也是NULL,那么有了这个assert就很安全了。
还有一点就是const保证了*s不会被不良修改。再一个就是size_t,size_t是无符号整型假如返回值是负数那么有了size_t就不会担心负数问题。
2.指针的使用和传址调用
2.1传值调用和传址调用
我们学指针是为了解决问题的,但什么问题是非指针不可呢?
例如;写一个函数交换两个整型变量的值
根据我们的理解而且是不用指针写出来的是这样的:想创建第三个变量来进行两个值的转换。
swap1函数在这个时候,是把变量本身直接传递给函数,这种调用函数的方式就是传值调用
但是呢,这样却没有交换了位置,为什么呢 ?
这里的ab地址和xy的地址不同,所以形参的交换对实参没有影响。
因为实参传递给形参的时候,形参会单独创建一份临时空间来接受实参,对形参的修改不影响实参 。
所以我们要怎么修改呢?这时就要用到指针了。
这里怎么理解呢?
这里传上去的是a和b的地址 ,我们知道创建一个变量的话,他们的地址都是随机的,但是呢,变量在销毁之前只要知道这个地址,那么这个变量就是可以访问的。而这里我们用int *px和int *py来接收&a和&b,就会使形参和实参的地址是一样的,而一样的话就会使形参转换实参也会给跟着转换。这就是传址调用。
下面是讲数组与指针的关系
3.数组名的理解
我们之前就知道数组名就是首元素的地址,地址上arr是等于&arr[0]的,但是这里有个疑问:
这串代码输出是40啊,如果是首元素的地址的话不应该是4或者8吗,为啥这里不一样呢?
其实数组名是首元素的地址这句话是对的,但是有两个例外:
sizeof(数组名),sizeof中单独放数组名,这里的数组名表示的是整个数组,计算的是整个数组的大小,单位是字节,
&数组名,这里的数组名表示的是整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)。
初次之外,任何地方使用数组名,数组名都表示首元素的地址。
这里补充几点,嘎嘎重点: &arr 是数组名取地址:
&arr 是对这个指针取地址,得到的是一个指向数组类型(数组元素类型和数组长度的复合类型)的指针。
&arr[0] 是直接对数组首元素取地址:
这样做是明确表示你想获取数组第一个元素的地址。
&arr[0] 得到的是一个指向数组第一个元素的指针。
在大部分情况下,当你需要对数组元素进行操作时,应该使用 &arr[0] 。
简单来说,虽然&arr和&arr[0]在值上是相同的(都是数组的起始地址),但是从语义上讲, &arr[0] 更清晰地表达了操作的意图,即获取数组的第一个元素的地址。这在进行指针运算或者作为函数参数传递时尤为重要。
我们在上面知道了arr和&arr和&arr[0]在值上是一样的都是数组起始地址但是是有区别的这里给串代码
这里我们发现&arr[0]和&arr[0]+1像差4个字节,arr和arr+1也是相差4个字节,是因为&arr[0]和arr都是首元素地址,+1就是跳过一个元素。
但是&arr和&arr+1相差40个字节,这就是&arr是数组地址,+1操作是跳过整个数组的。
但是这里的40是怎么算出来的呢?首先我们知道打印出来的地址是16进制的数,他们相差28,要把28转化成10进制的数就是40,这才是它们之间的地址真实差值,28只是在16进制是28并不是正儿八经的数差。
4.使用指针访问数组
什么是使用指针访问数组呢?
在编程中,使用指针访问数组通常意味着直接通过内存地址来访问数组元素,而不是通过数组索引。在C语言中,这种操作比较常见,因为C语言提供对内存的直接控制。
#include <stdio.h>
int main()
{
int arr[10] = {0};
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
int* p = arr;
for(i=0; i<sz; i++)
{
scanf("%d", p+i);
//这里的p+i可以写成arr+i
}
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));
}
return 0;
}
这串代码就是使用指针访问数组
我们再来分析一下:
*(p+i)=*(arr+i) 而*(arr+i) = arr[i]而且p是指针变量这里面存放着arr的地址所以p = arr,
所以arr[i] = p[i]还有神奇的*(arr + i)= *(i + arr)满足交换律因为*(arr+i)= arr[i]所以*(i+arr)= i[arr]真是神奇啊!
,