今天我们学习指针,我们现在来了解一下指针它是什么呢?
在计算机科学中,指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。
由于通过地址能找到所需要的变量单元,可以说地址指向该变量单元。因此将地址形象的称为“指针”。意思是通过它能找到以它为地址的内存单元。我们可以这么理解:
对应代码如下:
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a取地址,可以使用&操作符。将a的地址存放在变量p中,p就是一个指针变量
return 0;
}
总结: 指针就是用来存放地址的变量。
1、指针是存放地之才出现的,地址是为了标示一块地址空间的。
2、指针让地址有地方存放,指针让内存的访问更加方便。
3、指针的大小在32位平台是4个字节,在64位平台是8个字节。
指针和指针类型
我们来讨论一下指针的类型
我们都知道,变量有不同的类型,整形,浮点型等。那么指针有没有类型呢?
准确的来说是有的。
int num = 10;
p = #
要想将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那么它的类型是什么样的呢?
我们这里给出一个指针变量相应的类型。
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
其实这里可以看到,指针是有类型的,类型是:type + *的方式。
其实:
char*类型的指针是为了存放char类型变量的地址。
short*类型的指针是为了存放short类型变量的地址。
int*类型的指针是为了存放int类型变量的地址。
...
以此类推。
那我们想一下,为什么要这么做呢??
1.只要有类型的区分,就定出一定的规则,使得编码会更加严禁。
2.确定了指针运算的规律。
指针类型如何确定指针的运算的规律:
1.指针+-整数。
2.指针的解引用。
指针+-整数
#include <stdio.h>
int main()
{
int num = 10;
char *pc = (char *)#
int *pi = #
printf("%p\n", &num);
printf("%p\n", pc);
printf("%p\n", pc + 1);
printf("%p\n", pi);
printf("%p\n", pi + 1);
return 0;
}
总结:指针的类型决定了指针向前或向后走一步有多大(距离)。
指针的解引用
#include <stdio.h>
int main()
{
int num = 0x11223344;
char *pc = (char *)#
char *pi = #
*pc = 0x55;
*pi = 0;
return 0;
}
总结:
指针的类型决定了对指针解引用的这时候有多大权限(能操作几个字节)。
比如:char*的指针解引用就只能访问一个字节,而int*的指针的解引用就能访问4个字节。
二级指针
指针变量也是变量,是变量就有地址,那么指针变量的地址存放在哪里呢?
这就是二级指针。
对于二级指针的运算有:
1.*ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa。
int b = 20;
*ppa = &b;//等价于 pa = &b;
2.**ppa先通过*ppa找到pa,然后对pa进行解引用操作:*pa,那么找到的就是a。
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
指针表达式解析
当有代码
char ch = 'a';
char *cp = &ch;
那么下面的代码是什么意思?
&ch;
cp;
&cp;
*cp+1;
*(cp+1);
++cp;
cp++;
*++cp;
*cp++;
++*cp;
(*cp)++;
++*++cp;
++*cp++;
指针的运算
1.指针+-整数
2.指针-指针
3.指针的关系运算
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
指针-指针
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
在关系运算中
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
代码简化,将代码修改如下:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行,
标准规定:
允许指向数组的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
模拟实现strlen
三种方式
方式1:
int my_strlen(const char * str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
方式2 :
int my_strlen(const char * str)
{
if (*str)
return 0;
else
return 1 + my_strlen(str + 1);
}
方式3:
int my_strlen(char *s)
{
char *p = s;
while (*p != ‘\0’)
p++;
return p - s;
}
模拟实现strcpy
参考代码:
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while ((*dest++ = *src++))
{
;
}
return ret;
}
模拟实现strcat
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest)
{
dest++;
}
while ((*dest++ = *src++))
{
;
}
return ret;
}
模拟实现strstr
char *my_strstr(const char* str1, const char* str2)
{
assert(str1);
assert(str2);
char *cp = (char*)str1;
char *substr = (char *)str2;
char *s1 = NULL;
if (*str2 == '\0')
return NULL;
while (*cp)
{
s1 = cp;
substr = str2;
while (*s1 && *substr && (*s1 == *substr))
{
s1++;
substr++;
}
if (*substr == '\0')
return cp;
cp++;
}
}
模拟实现strcmp
int my_strcmp(const char * src, const char * dst)
{
int ret = 0;
while (!(ret = *(unsigned char *)src - *(unsigned
char *)dst) && *dst)
++src, ++dst;
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1;
return(ret);
}
模拟实现memcpy
void * memcpy(void * dst, const void * src, size_t count)
{
void * ret = dst;
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
模拟实现memmove
void * memmove(void * dst, const void * src, size_t coun
t)
{
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + cou
nt)) {
/*
* Non-Overlapping Buffers
* copy from lower addresses to higher addr
esses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addr
esses
*/
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return(ret);
}