1. 一级指针
1.1 int * s[] 和 int (*s)[]
-
int * s[]表示一个数组,数组里的元素都是指针变量,这些指针是整型指针(指向整型);
-
而int (*s)[]表示一个指针,该指针指向一个数组,这个数组里存放的是整型元素。
1.2 void类型指针
-
void不能定义变量,但是可以定义指针变量,void指针可以存放任意变量地址
-
如下面这段代码中p可以指向a、ft、dx的地址
int a = 10;
float ft = 12.33f;
double dx = 12.23;
void* p = nullptr;
p = &a;
p = &ft;
p = &dx;
//这里面的p称作泛型指针
但是泛型指针无法解析如下面代码所示
sizeof(p); //对
sizeof(*p); //不对
但是接着上面的代码,下面这样写就是错误的
int* ip = p;
这里面定义了ip整型指针,这个时候如果将p指针赋值给ip,那么需要对其强转,即将其强转为int *类型的整型指针
int *ip = (int *)p;
自己给自己赋值也可以,比如说将自己的地址赋值过来,所以void指针无二级指针等说法
p = &p;
在标准ANSI C中,void将指针+1的能力和指针解析的能力关闭了,但是打开了可以将任意类型指针赋值给void指针的大门
在GNU
标准中则允许,因为在默认情况下,GNU
认为void*
和 char*
一样,在这里sizeof(*p) ==sizeof(char)
1.3 泛型指针的应用举例
有三种类型的数组,现在要实现一个函数,使得三个数组初始化都适用,采用泛型指针概念实现
#include<stdio.h>
//size代表字节的个数
void my_setzero(void* ar,int size) {
char* cp = (char*)ar;
for (int i = 0; i < size; ++i) {
cp[i] = 0;
}
}
int main() {
int ar[10];
char cr[10];
double dr[10];
my_setzero(ar, sizeof(ar));
my_setzero(cr, sizeof(cr));
my_setzero(dr, sizeof(dr));
return 0;
}
其原理是一个字节一个字节初始化数组,这样最后都会初始化为0
2. 二级指针
二级指针其所占大小还是四个字节。
2.1 对于二级指针的理解
对于下面这个程序段
int a =10,b = 20;
int *ap = NULL;
int **s = NULL;
s = ≈
*s = &a;
**s = 100;
*s &b;
**s = 200;
首先定义了一个一级指针ap
,然后定义了一个二级指针s
,然后将ap
地址给s
,那么此时*s
里面存放的就是ap
这个指针,**s
指的是*ap
,下面一段程序同理。
s
代表p0
地址
*s
代表a0
地址
**s
表示a0
s+1
表示挪到p1
位置
上图说明了数组名和数组名取址二者区别在哪里,由监视来看,对于ar0
和&ar0
来说,其地址都是一样的,但是二者含义不一样,对于ar0
来说,其代表的是数组首元素的地址,而对于&ar0
代表整个数组,从二者加1之后的地址变化可以看出,对于ar0+1
后,其移动了4个字节,表示只移动了一个整型元素所占的空间大小,对于&ar0+1
,移动了整个数组4*4
的字节数,注意这里面0x
代表16进制
,所以这里面从7
变成了8
上图右侧是一个二维数组,在内存中的部分地址空间,这个时候如果想要用指针来实现,每次移动到二维数组的下一个一维数组的元素,那么用下面这段代码中 p指针
是不行的,但是采用下面的 s指针
却是可以的
int* p = ar;
//由于ar里面也是int类型的元素
//如果执行p++只会移动到ar[0][1],不会移动到ar[1][0]
//如果想要实现这一功能,需要采用一个指向ar0中一维数组元素的指针
//由于这里面的ar[3][4]
//所以需要定义一个指向一维整型数组,且元素个数为4的指针
int(*s)[4];
s++;
//会移动到ar[1][0
s+1
指的是移动到了p1
,其移动了四个字节的空间(这里面指的是移动了一个指针的空间)
*s+1
指的是移动到了a1
,其移动了四个字节的空间(这里面指的是移动了一个整形的大小空间)
**s+1
指的是a0+1
如果将这里面的整型
变成char
类型,那么每次移动的空间会不一样
s+1
指的是移动到了p1
,其移动了四个字节的空间(这里面指的是移动了一个指针的空间)
*s+1
指的是移动到了a1
,其移动了一个字节的空间(这里面指的是移动了一个char类型的大小空间)
**s+1
指的是a0+1
3. 函数库函数实现
3.1 字符串函数实现
3.1.1 计算字符串长度
两种方法实现这个函数
第一种方法
:用计数器来实现
#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str) {
if (str == nullptr)return -1;
int index = 0;
while (str[index] != '\0') {
index++;
}
return index;
}
第二种方法
:用指针相减来实现
#include<stdio.h>
#include<assert.h>
int my_strlen_1(const char* str) {
assert(str);
const char* cp = str;
//若char* start = str;
//会报错,这里面是不能兼容问题,需要加一个const关键字
//否则就会出现能力被扩展功能
while (*cp != '\0') {
cp++;
}
return cp - str;
}
3.1.2 拷贝函数
用两种方法实现拷贝函数
第一种方法:
void my_strcpy(char* dist, const char* src) {
if (dist == nullptr || src == nullptr)return;
int i = 0;
while (src[i] != '\0') {
dist[i] = src[i];
i++;
}
dist[i] = '\0';
}
第二种方法:
//不用下标,用指针实现字符串拷贝
void my_strcpy_1(char* dist, const char* src) {
/*
while ((*dist = *src) != '\0') {
dist++;
src++;
}
*/
while ((*dist++ = *src++) != '\0');
}
3.1.3 链接函数
char* my_strcat(char* dist, char const* src)
{
char* p = dist;
while (*dist) {
dist++;
}
while (*dist++ = *src++);
*dist = '\0';
return p;
}
3.1.4 字符串比较函数
int my_strcmp(const char* first, const char* second)
{
assert(first && second);
while (*first == *second)
{
if (*first == '\0')
{
return 0;
}
first++;
second++;
}
return (*first - *second);
}