指针的概念:指针就是地址,它的值指向存在电脑存储器中的另一个值(通过指针找到以它为地址的内存单元)。
二级指针:指针变量也是变量,对于变量都有对应的地址,存放指针变量的地址就是二级指针
为什么存在指针:
计算机是将内存分成很多小的单元,每个单元对应一个独一无二的地址,所以一个地址就对应一个空间。
对于一个32位的机器,假设有32根地址线,假设每根地址线再寻址的是产生一个电信号(0或1),那么32位地址线产生的就会是
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
.........
11111111 11111111 11111111 11111111
总共为2^32个地址,若每个地址一个字节,那么将给4G的空间进行编址
若为32位地址线,一个指针变量的大小将是4个字节,若为64位的地址线,一个指针变量的大小将是8个字节,才能存放一个地址
指针变量的赋值:
1.指针变量同其他变量一样使用之前不仅要定义还需要说明,而且必须赋予具体的值,如果未经赋值的指针变量不能使用,赋值将使系统混乱, 甚至死机。
2.指针变量必须赋予地址,不能赋值其他数据,否则将引起其他错误。
在c语言中,变量的地址是由编译系统分配的,对用户完全透明,用户不知道变量的具体地址,所以提供了&(地址运算符)来表示变量的地址。
&b表示变量b的地址
指针变量初始化的两种方法:
(1)int a;int *p = &a;
(2)int *p;p = &a;
指针的运算:
指针+-整数:指针变量+-整数n就是将当前指针指向的位置向前或向后移动n个位置。
指针-指针(俩指针不能进行加法运算):俩个指针所指数组元素之间的相差的元素个数。
指针关系运算:
若p1,p2是定义的俩指针变量
p1 == p2 :p1和p2指向一数组元素。
p1>p2:表示p1处于高地址位
p1<p2:表示p1处于低地址位
利用指针模拟实现strlen函数
strlen函数功能:strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值(长度不包含'\0')。
int my_strlen1(const char* str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
//方法二
int my_strlen2(const char* str)
{
if (*str == NULL)
{
return 0;
}
else
{
return 1 + my_strlen2(str + 1);
}
}
//方法三
int my_strlen3(const char * str)
{
char * p = str;
while (*p != '\O')
{
p++;
}
return p - str;
}
模拟实现strcpy
strcpy函数功能:字符串的复制,从src地址开始含有“\0”结束符的字符串复制到以dest为起始的地址空间,返回值为char*
//模拟实现strcpy
char * my_strcpy(char * p1, char* p2)
{
assert(p1 != NULL);
assert(p2 != NULL);
char * ret = p1;
while ((*p1++ = *p2++) != '\0')
{
;
}
return ret;
}
模拟实现strcat
strcat函数的功能:实现俩个char类型的连接,d和s所指的内存区域不可以重叠,而且d必须有足够打的空间来容纳s,返回指向d的指针。
//模拟实现strcat
char * my_strcat(char * dest, char * src)
{
assert(dest != NULL);
assert(src != NULL);
char * ret = dest;
while (*dest != '\0')
{
dest++;
}
while ((*dest++ = *src++) != '\0')
{
;
}
return ret;
}
模拟实现strstr函数
strstr(p1,p2)函数的功能:判断p2是否为p1的子串,如果是则返回首次出现的地址,如果不是则返回NULL。
char * my_strstr(const char * p1, const char * p2)
{
assert(p1 != NULL);
assert(p2 != NULL);
char * cp = (char*)p1;
char * substr = (char*)p2;
char * s1 = NULL;
if (*p2 == '\0')
{
return NULL;
}
while (*cp != '\0')
{
s1 = cp;
substr = p2;
while (*s1 && *substr && (*s1 == *substr))
{
s1++;
substr++;
}
if (*substr == '\0')
{
return cp;
}
cp++;
}
}
模拟实现strcmp函数
strcmp函数的功能:用于比较俩个字符串,相同返回0,不同返回随机数。
int my_strcmp(const char * p1, const char * p2)
{
int ret = 0;
while (!(ret = *(unsigned char *)p1 - *(unsigned char *)p2) && *p2 && *p2)
{
p1++;
p2++;
}
if (ret == 0)
{
return 0;
}
if (ret < 0)
{
return -1;
}
return 1;
}
模拟实现mencpy和memmove
俩个函数的功能:都为内存拷贝函数,从源src所指内存地址的起始位置拷贝n个字节到dest所指的内存空间中去。
首先理解memcpy和memmove的区别:俩函数都是c语言中的内存拷贝函数,mencpy不可以处理内存重叠,memmove相当于memcpy的优化版。
首先看代码:
void Test()
{
char buf[20] = "12345";
char ret[20] = "12345";
printf(">%s\n", buf);
memmove(buf+2 , buf, strlen(buf));
printf(">%s\n", buf);
memcpy(ret+2, ret, strlen(ret));
printf(">%s\n", ret);
}
运行结果

模拟实现代码:
void * my_memcpy(void * dst, const void * src, size_t count)
{
void * ret = dst;
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return ret;
}
//模拟实现memmove
void * my_memmove(void * dst, const void * src, size_t count)
{
void * ret = dst;
if (src >= dst || (char*)src + count <= (char*)dst )
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--)
{
*(char*)dst = *(char*)dst;
dst = (char*)dst - 1;
src = (char*)src - 1;
}
}
return ret;
}
根据memcpy的源代码可以看出,memcpy在从前向后拷贝,如果内存重叠造成拷贝错误,在从后拷贝中不会出现错误,memmove是则是进行判断,如果内存重叠,且为从前向后拷贝则变为将从需要拷贝的内存最末尾向前依次减减去向目标地址+count依次减减拷贝。