指针基本介绍
计算机中的每个内存都有地址
整型分配4字节,字符分配1字节 ,浮点数分配4字节
指针是一个变量,它存放着另外一个变量的地址
int a;
int *p;
p = &a;//
a = 5;
printf(p) //get a address
print &a //get a address
print &p //get p address
print *p //得到指针所指向的地址的值
p = address
*p = value at address
当指针变量前面没有加*号进行操作,是在对地址进行操作,
*p则是对指针所指向的地址操作
指针代码示例
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 10;
int *p = &a;
//p = &a;//&a = address of a
//int* p 意外着指向整型的指针然后写出变量名
printf("Address of p is %d\n",p);
printf("Value at p is %d\n",*p);
printf("%d\n",p);
printf("%d\n",*p);//*p = value at address pointed by p
printf("%d\n",&a);
printf("a = %d\n",a);
*p = 12;//使用指针对所指向的地址值进行修改
printf("a = %d\n",a);
int b = 20;
*p = b;
printf("Address of p is %d\n",p);
printf("Value at p is %d\n",*p);
system("pause");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 10;
int *p;
p = &a;
//Pointer arithmetic
printf("Address p is %d\n",p);
printf("value at address p is %d\n",*p);
printf("size of integer is %d bytes\n",sizeof(int));
printf("Address p+1 is %d\n",p+1); //增加4字节以得到下一个整型的地址
system("pause");
return 0;
}
指针的类型,算术运算,void指针
指针是强类型的
我们需要一个特定类型的指针变量来存放特定类型变量的地址
byte3 | byte2 | byte1 | byte0 |
00000000 | 00000000 | 00000100 | 00000001 |
203 | 202 | 201 | 200 |
最左边为符号位 | 2的10次方 | 2的一次 |
剩下的31个位用来存储值
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 1025;
int *p;
p = &a;
//Pointer arithmetic
printf("size of integer is %d bytes\n",sizeof(int));
printf("Address = %d,value = %d\n",p,*p);
char *po;
po = (char*)p;
printf("size of char is %d bytes\n",sizeof(char));
printf("Address = %d,value = %d\n",po,*po);
printf("Address = %d,value = %d\n",po+1,*(po+1));
system("pause");
return 0;
}
通用指针类型,不针对某个特定的数据类型,这种指针类型被称为void类型的指针
void *p
指向指针的指针(pointer to pointer)
#include <stdio.h>
#include <stdlib.h>
int main() {
int x = 5;
int *p = &x;
*p = 6;
int **q = &p;
int ***r = &q;
printf("%d\n",*p);//指针p所指向的内存地址的值
printf("%d\n",*q);//指针q所指向的指针p的地址
printf("%d\n",*(*q));//指针q所指向的指针q的地址,指针p所指向的地址值
printf("%d\n",*(*r));
printf("%d\n",*(*(*r)));
//变量x是整型,为了得到x的地址,需要int* 类型的指针
//为了存储p地址,需要一个指向int*类型的指针,为此再加一个*,表示这个指针指向的是int*
//可以无限套娃
system("pause");
return 0;
}
函数传值vs传引用
当我们在函数里面声明一个变量,我们把它叫做局部变量,我们只能在声明了变量的地方使用这个变量
#include<stdio.h>
#include<stdlib.h>
void Increment(int a)
{
a = a + 1;
printf("Address of variable a in increment = %d\n",&a);
}
int main()
{
int a;
a = 10;
Increment(a);
printf("Address of variable a in main = %d\n",&a);
system("pause");
return 0;
}
main函数的a与自增函数的a的地址不一样
应用程序所使用的内存如下表格
Heap |
Stack |
Static/Global |
Code(Text) |
第一部分(Code):用来存储程序的指令,计算机需要把指令加载到内存,就像上面程序中的自增语句
第二部分:分配给静态或者全局变量
如果我们不是在函数中声明变量,那么它将会是一个全局变量(Global),作为一个全局变量,在程序的任何地方都可以访问和修改。
局部变量只能在特定的函数或者特定的代码块进行访问和修改。
第三部分:局部变量都放在stack部分
第四部分:堆
内存中的四个部分,一二三是固定的,应用程序在运行时可以要求在堆区为它分配跟多的内存
当我们在主函数中调用其它函数,这个参数被称为实参,被调函数中的参数被称为形参,调用时,实参被映射到形参
传引用
#include<stdio.h>
#include<stdlib.h>
void Increment(int *p)
{
*p = (*p) + 1;
}
int main()
{
int a;
a = 10;
Increment(&a);
printf("a = %d\n",a);
system("pause");
return 0;
}
指针和数组
#include<stdio.h>
#include<stdlib.h>
int main()
{
int A[] = {2,4,5,8,1};
printf("%d\n",A);
printf("%d\n",&A[0]);
printf("%d\n",A[0]);
printf("%d\n",*A);
system("pause");
return 0;
}
#include<stdio.h>
#include<stdlib.h>
int main()
{
int A[] = {2,4,5,8,1};
int i;
for(i = 0;i < 5;i++)
{
printf("Address = %d\n",&A[i]);
printf("Address = %d\n",A+i);
printf("Value = %d\n",A[i]);
printf("value = %d\n",*(A+i));
}
system("pause");
return 0;
}
数组作为函数参数
#include<stdio.h>
#include<stdlib.h>
int SumOfElements(int A[],int size)
{
int i,sum = 0;
for(i = 0;i < size;i++)
{
sum+= A[i];
}
return sum;
}
int main()
{
int A[] = {1,2,3,4,5};
int size = sizeof(A)/sizeof(A[0]);
//用数组A的大小除以A[0]的大小,这样就会得到数组中元素的个数
int total = SumOfElements(A,size);
printf("Sum of elements = %d\n",total);
system("pause");
return 0;
}
#include<stdio.h>
#include<stdlib.h>
int SumOfElements(int A[])//A是一个整型指针,而在main函数中A是一个数组
{
int i,sum = 0;
int size = sizeof(A)/sizeof(A[0]);
printf("SOE - Size of A = %d,size of A[0] = %d\n",sizeof(A),sizeof(A[0]));
//'sizeof' on array function parameter 'A' will return size of 'int*'
//当编译器看到数组作为函数参数的时候,它不会拷贝整个数组,这里我们不是拷贝变量的值,而仅仅是拷贝变量的地址
for(i = 0;i < size;i++)
{
sum+= A[i];
}
return sum;
}
int main()
{
int A[] = {1,2,3,4,5};
//用数组A的大小除以A[0]的大小,这样就会得到数组中元素的个数
int total = SumOfElements(A);
printf("Sum of elements = %d\n",total);
printf("Main - Size of A = %d,size of A[0] = %d\n",sizeof(A),sizeof(A[0]));
system("pause");
return 0;
}
指针和字符数组
NULL字符的ASCII的值是0,因为C里面的字符串必须以NULL结束
#include<stdio.h>
#include<stdlib.h>
int main()
{
char C[5];
C[0] = 'J';
C[1] = 'O';
C[2] = 'H';
C[3] = 'N';
C[4] = '\0';
printf("%s",C);
getchar();
return 0;
}
strlen函数不会把null算入在内
当把字符数组赋值给字符指针,实际上是把字符数组的首地址告诉指针
#include<stdio.h>
void print(char* C)
{
int i = 0;
while(C[i] != '\0')
{
printf("%c",C[i]);
i++;
}
printf("\n");
}
int main()
{
char C[20] = "Hello";
print(C);
getchar();
return 0;
}
指针和二维数组
指针和多维数组
多维数组本质上是数组的数组
数组基本上可以理解为同类型事物的集合,多维数组基本上可以理解为数组的集合
当仅使用数组名的时候,它会返回数组首元素的指针
print B //400
print *B //400
print B[0] //400
print &B[0][0] //400
B返回一个指向一维数组的指针,而*B返回一个指向整型的指针
当我们只是打印地址的时候,一维数组B[0]和B[0]的首元素的起始地址是一样的,所以会打印相同的地址
arr[2][3][4],相当于一个三维数组有两个数组,这两个二维数组里面各自有三个数组,这三个数组各自里面再有四个数组
解引用就是 当前变量存取的值,如果*p是指向数组,则值就是首地址,如果是指向数据,则就是值。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int C[3][2][2]={{{2,5},{7,9}},{{3,4},{6,1}},{{0,8},{11,13}}};
printf("%d %d %d %d",C,*C,C[0],&C[0][0]);
printf("%d\n",*(C[0][0]+1));
system("pause");
return 0;
}
多维数组作为参数传给函数
指针和动态内存-栈vs堆
栈,用来存放函数调用的所有信息和所有局部变量
局部变量是在函数内部声明的,只在函数执行期间存活
不在函数中声明的变量,它们的生命周期贯穿整个应用程序
任何时候都是栈顶的函数在执行
应用程序的堆不是固定的,它的大小在应用程序的整个声明周期是可变的
这里所说的堆和数据结构中的堆没有关系,此处的堆只是用来描述空闲的内存池
栈也是一种数据结构,但是栈区实际上就是栈这种数据结构的一种实现,堆并不是这样
内存在栈上的分配和销毁有一定的规则 ,当一个函数被调用的时候,它被压入堆栈,当它结束的时候,弹出堆栈。
如果变量是在栈上分配的,我们不能操作变量的范围
不能操控变量的范围,就是比如说定义了一个可变数组在栈中,而在程序运行中栈的内存不会增长,因此你的数组很有可能因为长度过长而造成栈溢出
应用程序的堆不是固定的,它的大小在应用程序的整个声明周期是可变的,也没有特定的规则来分配和销毁相应的内存
堆也被称为动态内存,使用堆内存意味着动态内存分配
堆也是一种数据结构,但是与c中的堆不一样
任何使用malloc分配的内存,最终通过调用free进行释放
c++用new来代替malloc ,delete来代表free