龙空技术网

11.C语言-指针1

技术老男孩 107

前言:

现在我们对“c语言中使用变量必须先什么后什么”大概比较珍视,我们都需要知道一些“c语言中使用变量必须先什么后什么”的相关资讯。那么小编也在网络上网罗了一些对于“c语言中使用变量必须先什么后什么””的相关知识,希望我们能喜欢,看官们快快来了解一下吧!

C语言最强,最容易出错的功能

内存是由按顺序编号的一系列存储单元组成的,在内存中,每个存储单元都由难一的地址,通过地址可以方便地在内存单元中存储信息。

#include<stdio.h>void main() {  printf("%p\n", main);//首地址  int num;  num = 8;  printf("%p", &num);  getchar();}

函数就是代码,变量就是数据

CPU执行流程

程序要进行的操作对应的代码被装载到代码区。

全局和静态数据等装载到数据区

开辟堆栈,供临变量等使用

指针:一个变量的地址

指针变量:专门存放变量地址的变量

void main() {  int num;  num = 8;  printf("%p", &num);  printf("%d", num);  getchar();}

可以手动在内存中编辑这个值

#include<stdio.h>void main() {  int num;  num = 8;  printf("%p", &num);  printf("%d\n", num);  printf("%d", *(&num));//这里就是通过*去读取对应地址的内容  getchar();}
&与*的关系
#include<stdio.h>void main() {  int num = 100;  int* p = #  printf("%d,%d\n", num, *p);  printf("%p,%p", p, &num);}
#include<stdio.h>void main() {  int num = 100;  int* p = # //一个指向int类型的指针变量,可以存一个int的变量  printf("%d,%d\n", num, *p);  printf("%p,%p\n", p, &num);  *p = 200;//这个对应修改了num  printf("%d,%d", *p, num);}

*p等价于num,p等价与&num

&取地址,*指地址中的内容

为什么要给*加上一个类型,这里是可以确定要从内存中什么地方读多长,int是4个字节,double就是8个了,char只是一个了。

#include<stdio.h>void main() {  int num1 = 100;  int num2 = 200;  int* p = &num1;  *p = 99;  printf("%d\n", num1);  p = &num2;//修改了地址,将p的地址修改成了num2的地址  *p = 199;  printf("%d,%d", num1, num2);}

传入地址,指针可以在函数内修改一个外部变量的值

#include<stdio.h>void edit(int *x) {  *x = 99;}void main() {  int x = 100;  edit(&x);  printf("%d", x);}

一个修改内存的例子

可以使用 __declspec(dllexport) 关键字从 DLL 中导出数据、函数、类或类成员函数。

#include<stdio.h>#include<Windows.h>void main() {  int x = 0;  printf("%p\n", &x);  while (1)  {    x++;    Sleep(5000);    printf("%d\n", x);  }}

将下面Dll注入到上面程序

#include<stdio.h>#include<stdlib.h>#include<Windows.h>//一个程序,不可以随便读取另外一个程序的内存//window内部,进程之间不能直接访问_declspec(dllexport) void DllMain() {  int* p =(int *) 0x000000D95850F5C4;  *p = 800;  MessageBoxA(NULL,"a","d",MB_ICONERROR|MB_YESNOCANCEL);}
指针与内存
#include<stdio.h>#include<Windows.h>void main() {  int num = 100;  int* p;  p = num;//这里一定想明白,p是一个地址,这样编译不会出错,但运行会出错  p = #  printf("%d", *p);}

修改一下,观察内存中的变化

void main() {  int num = 100;  int* p;  p = num;//这里一定想明白,p是一个地址,这样编译不会出错,但运行会出错  p = #  printf("%p", &p);}

*p对应的是数值,&p是地址

指针的大小

void main() {  int* p1;  double* p2;  char* p3;  printf("%d,%d,%d", sizeof(p1), sizeof(p2), sizeof(p3));  getchar();}

这里对应64位机器,输出都是8位,这里能想明白吗?因为他是地址,所以大小固定。

所谓指针,指的是“储存的内容是地址的量”,两个要点:一、指针是个量,对应着一块内存区域,二,指针存储的信息是某个内存单元的地址。

指针变量声明

C 语言规定所有变量在使用前必须先定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是专门用来存放地址的,所以必须将它定义为“指针类型”。

基类型 *指针变量名;

int *i;float *j;

“*” 表示该变量的类型为指针类型。指针变量名为 i 和 j,而不是 *i 和 *j。

void main() {  int* p1, p2, p3;  printf("%d,%d,%d", sizeof(p1), sizeof(p2), sizeof(p3));}

看看这个,p1对应8,而p2,p3是4,这是因为64位下指针固定就是8,另外两个并不是指针。

void main() {  int* p = NULL;//空,不是0,不指向任何变量  if (p == NULL) {    printf("p is null");  }  else  {    printf("ps not is null");  }}
间接与直接调用
void main() {  int num = 100;  printf("%d,%p\n", num, &num);//直接  *(&num) = 99;//根据地址取值,间接  printf("%d,%p\n", num, &num);  int* p = #//p 是指针  *p = 98;//赋值,这里*p等价于num  printf("%d,%p", *p, p);}

直接访问:按变量地址存取变量值

间接访问:通过存放变量地址的变量去访问变量

理解一下scanf输入指针地址

void main() {  int num1;  scanf_s("%d", &num1);  printf("%d,%p\n", num1, &num1);  int num2;  int* p=NULL;  scanf_s("%p", &p);//这里输入一个地址,将&num1的地址给它  printf("%d,%p", *p, p);}
不修改变量值,通过指针交将数据
#include<stdio.h>#include<Windows.h>void main() {  int a, b;  int* pa, * pb;  a = 1000;  b = 500;  if (a > b) {    pa = &b;    pb = &a;  }  else  {    pa = &a;    pb = &b;  }  printf("%d,%d", *pa, *pb);}
指针与函数参数
#include<stdio.h>#include<Windows.h>void change(int* address) {  *address = 5;//&取出地址,*根据地址找值  printf("change:%d,%p\n",*address,&address);}void main() {  int num = 10;  printf("main:%d,%p\n", num, &num);  change(&num);  printf("main:%d,%p\n", num, &num);}

函数调用,改变原数据,传地址,可以改变,传数据,无法改变。

数组作为参数,传递的是指针

//数组作为参数,传递的是指针//在32位系统(即x86)中,指针的大小为4字节。//在64位系统(即x64)中,指针的大小为8字节。void show(int a[5]) {  printf("show:%d\n", sizeof(a));}void main() {  int a[10] = { 1,2,3,4,5 };  printf("main:%d\n", sizeof(a));  show(a);}

这里修改就是指针

void show(int a[5]) {  printf("show:%d\n", sizeof(a));  for (int i = 0; i < 5; i++)  {    a[i] = i + 2;  }}void main() {  int a[10] = { 1,2,3,4,5 };  printf("main:%d\n", sizeof(a));  show(a);  for (int i = 0; i < 5; i++)  {    printf("a[%d]=%d\n", i, a[i]);  }}
指向指针的指针基础

指针变量也是变量,占据一定的内存空间有地址,因此可以用一个指针指向它,这称为指向指针的指针,或二级指针。

有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:

#include<stdio.h>#include<Windows.h>void main() {  int a = 100;  int* p1 = &a;  int** p2 = &p1;  printf("%p,%p", p1, p2);}

指针类型决定从地址开始取多少位

void main() {  double d1 = 98.23;  double* p = &d1;  double** p1 = &p;  //88 f8 2f 5a ef   printf("%f,%p,%p", *p,p1,p);}

在x64下有一个错误是看不出来的,在32位,或c++下会出错

这里&p是一个地址,将它放入一个double *中是不对的,数据长度不同。

&p对应的就double**

void change(double** p) {  **p = 88.2;  printf("change:%f,%p\n", **p,*p);}void main() {  double d1 = 98.23;  double* p = &d1;  change(&p);  printf("main:%f,%p\n", *p,p);}

这里比较明显看到**p就是一个二级指针,它指向的就是*p的地址。

改变外部变量,如果是一个数据,传递数据的地址(指针),如果是一个指针,传递指针的地址。

注入二级指针

目标代码

#include<stdio.h>#include<Windows.h>void main() {  int num = 100;  char level1 = 'A', level2 = 'B', level3 = 'C';  char* level = &level1;  //这里需要知道*level的地址  printf("%p,%p,%p,%p,%p\n", &num, &level,&level1,&level2,&level3);  while (1)  {    printf("num=%d,level=%c\n", num, *level);    Sleep(3000);  }}

注入代码

#include<stdio.h>#include<windows.h>_declspec(dllexport) void DllMain() {  int* p = (int*)0x00000058F24FF544;  *p = 9999;  char** l = (char*)0x00000058F24FF5C8;  *l= 0x00000058F24FF584;}

再用RemoteDll注入

这里不会出错,但写法是不对的,l2需要是**的二级地址。

#include<stdio.h>#include<Windows.h>void main() {  char l = 'A';  char* l1 = &l;  char* l2 = &l1; //这里编译没有出错,但有警告提示,这个是个错误,一定要记录指针长度是8(x64)  printf("%d,%d\n", sizeof(l1), sizeof(l2));  printf("%p,%p\n", l1, l2);}
指针的算术运算

指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

作为一种特殊的变量,指针可以进行一些运算,但并非所有的运算都是合法的,指针的运算主要局限在加减算术和其他一些为数不多的特殊运算

void main() {  int x = 5;  int* p = x;//这里是不对的,5会被当成地址}
void main() {  int x = 5;  int* p1 = &x;  int* p2 = p1;  //p1,p2都会指向x的地址  printf("%p,%p", p1, p2);}

这里查看一下指针地址变化,*p++在循环指针时int就是4位一变,double是8位一循环

void main() {  int a[5] = { 1,2,3,4,5 };  printf("%p\n", a);  for (int  i = 0; i < 5; i++)  {    printf("%d,%d\n", a[i], *(a + i));  }  printf("int:%d\n", sizeof(int));  printf("double:%d\n", sizeof(double));  //int长度是4  for (int *p = a; p < a+5; *p++)  {    printf("%p\n", p);    *p = *p + 1;    printf("%d\n", *p);  }}
void main() {  int a[5] = { 1,2,3,4,5 };  //int长度是4  int* p = a;  for (int i = 0; i < 5; i++)  {    printf("%d,%p\n", *p, p);    p++;  }}

地址传递

void main() {  int x = 10;  int* p = &x;  printf("%d\n", x);  int* p1 = p;//地址变化  *p1 = 12;  printf("%d\n", x);}
指针相减

指针和指针可以做减法操作,但不适合做加法运算;

指针与指针的相减操作,表示两个指针指向的内存位置之间相隔多少个元素(注意是元素,并不是字节数)。

#include<stdio.h>void main() {  int ary[5] = { 1,2,3,4,5 };  int* p1 = &ary;  int* p2 = &ary[4];  int count = p2 - p1;  printf("%d", count);}

即是两个之间之间的元素数目为 4 个。

不同类型的指针不允许相减

#include<stdio.h>void main() {  int ary[5] = { 1,2,3,4,5 };  int* x1 = &ary[3] - 1;  printf("%d,%p", *x1,x1);}

标签: #c语言中使用变量必须先什么后什么 #c 指针加1