学习C肯定会碰到指针,指针是C的灵魂。所以学好指针很关键,这里写一些指针方面的容易错的或者易混淆的知识点还有自己不会的盲点,以便之后复习时用。
1.数组传参和数组形参
1.1数组传参
如果函数遇到数组传参的,不论是什么形式的形参,只要是数组,那么被调函数都将这个形参都当做指针来使用。
#include "stdio.h"
PrintArray(int *a, int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
int main(void)
{
int a[] = { 3,4,5,6,7,7,8 };
int num = sizeof(a) / sizeof(a[0]);
PrintArray(a, num);
system("pause");
}
一般我们使用数组传参都是的。
下面还有一种形参表示方法也是常用的。效果其实是一样的。
#include "stdio.h"
PrintArray(int *a, int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
PrintArray1(int a[], int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
int main(void)
{
int a[] = { 3,4,5,6,7,7,8 };
int num = sizeof(a) / sizeof(a[0]);
PrintArray(a, num);
PrintArray1(a, num);
system("pause");
}
甚至还有一种方法
#include "stdio.h"
PrintArray(int *a, int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
PrintArray1(int a[], int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
PrintArray2(int a[7], int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
int main(void)
{
int a[] = { 3,4,5,6,7,7,8 };
int num = sizeof(a) / sizeof(a[0]);
PrintArray(a, num);
PrintArray1(a, num);
PrintArray2(a, num);
system("pause");
}
1.2数组传参实质
数组实质传的都是指针,不论什么表现形式。
#include "stdio.h"
PrintArray(int *a, int num)
{
int i,num1;
num1 = sizeof(a) / sizeof(a[0]);
printf("num = %5d",num1);
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
PrintArray1(int a[], int num)
{
int i,num1;
num1 = sizeof(a) / sizeof(a[0]);
printf("num = %5d", num1);
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
PrintArray2(int a[7], int num)
{
int i,num1;
num1 = sizeof(a) / sizeof(a[0]);
printf("num = %5d", num1);
for (i = 0; i < num; i++)
{
printf("%5d", a[i]);
}
printf("\n");
}
int main(void)
{
int a[] = { 3,4,5,6,7,7,8 };
int num = sizeof(a) / sizeof(a[0]);
PrintArray(a, num);
PrintArray1(a, num);
PrintArray2(a, num);
system("pause");
}
可以看到num1的值均为1。
实参都是数组,形参都是指针,无论什么形式。
2.数组首元素地址和整个数组地址
#include "stdio.h"
int main(void)
{
int a[10];
printf("a的大小 = %d\n", sizeof(a));
printf("a = %d\n", a);
printf("a + 1 = %d\n", a + 1);
printf("&a = %d\n", &a);
printf("&a + 1 = %d\n", &a + 1);
system("pause");
}
从这组输出我们可以看到几点:
- a数组大小是40。因为一个int是4字节,一共10个。
- 数组a和&a值一样
- 但是a是数组首元素地址,而&a是整个数组地址。
- 这个原因也导致另外两个值不同。a+1就是第二个元素地址,偏移4字节
- &a+1是相当于偏移整个数组大小,也就是40字节。
3.变量在内存中的位置
变量在内存中有四个地方可以存放,读取修改变量实质都是通过指针修改。
3.1变量存放的区域
栈区 (stack) | 程序局部变量,编译器自动分配 |
---|---|
堆区(heap) | Malloc new free delete 操作系统来管理 一般有程序员动态分配 |
全局区/静态区(static) | 全局变量和静态变量存放地方,程序结束后由操作系统释放 |
常量区 | 常量存放区域,程序结束后由操作系统释放 |
3.2常量例子
#include "stdio.h"
char *GetChar1(void)
{
char *p1 = "abcdefg";
return p1;
}
char *GetChar2(void)
{
char *p2 = "abcdefg1";
return p2;
}
int main(void)
{
char *p1 = NULL;
char *p2 = NULL;
p1 = GetChar1();
p2 = GetChar2();
//打印数据
printf("p1 = %s\n", p1);
printf("p2 = %s\n", p2);
//打印地址
printf("p1 = %5d\n", p1);
printf("p2 = %5d\n", p2);
system("pause");
}
在内存中表示为
因为GetChar函数里面都是指针所以都放在栈区,而常量都在全局区。
当被调函数return之后,上面两个指针被释放,然后将地址赋给main函数里面的指针,他们就指向相应的值。
接下来,还有一个易错的点!!!
#include "stdio.h"
char *GetChar1(void)
{
char *p1 = "abcdefg1";
return p1;
}
char *GetChar2(void)
{
char *p2 = "abcdefg1";
return p2;
}
int main(void)
{
char *p1 = NULL;
char *p2 = NULL;
p1 = GetChar1();
p2 = GetChar2();
//打印数据
printf("p1 = %s\n", p1);
printf("p2 = %s\n", p2);
//打印地址
printf("p1 = %5d\n", p1);
printf("p2 = %5d\n", p2);
system("pause");
}
main函数里面指针地址是一样的
3.3堆和栈例子
用户自己申明的变量是在堆中存放的,只有用户释放。
#include "stdio.h"
char *GetString(int num)
{
char *s = NULL;
s = (char *)malloc(sizeof(char)*num);
if (s == NULL)
{
return NULL;
}
return s;
}
int main(void)
{
char *p1 = NULL;
p1 = GetString(10);
if (p1 == NULL)
{
return;
}
strcpy(p1, "abcdefg");
printf("%s\n", p1);
system("pause");
}
内存中分布:
当GetString()被调用后,s释放之后,malloc分配的空间还是存在,所以还能继续打印。
接下来的问题在实际写的过程中会犯错:
#include "stdio.h"
char *GetString(int num)
{
char *s = NULL;
s = (char *)malloc(sizeof(char)*num);
if (s == NULL)
{
return NULL;
}
return s;
}
char *GetString1(void)
{
char s1[10];
strcpy(s1, "qwerty");
printf("%s\n", s1);
return s1;
}
int main(void)
{
char *p1 = NULL;
char *p2 = NULL;
p1 = GetString(10);
if (p1 == NULL)
{
return;
}
p2 = GetString1();
strcpy(p1, "abcdefg");
printf("%s\n", p1);
printf("%s\n", p2);
system("pause");
}
这时的p2其实打印不出来或者程序死机了。这是因为GetString1()中的s1变量是申明在栈区中,当调用结束后,被释放了,那块区域中的qwerty就没了。但是地址还是那个10字节的起始地址,并没有全部return出来。
4.其他
内存中的栈是开口向下的。