学习C指针

指针基本介绍

计算机中的每个内存都有地址

整型分配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

指针和动态内存

相关推荐

  1. C/C++ 学习笔记】指针

    2024-01-13 12:54:06       24 阅读
  2. C语言学习(6)—— 指针

    2024-01-13 12:54:06       31 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-13 12:54:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-13 12:54:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-13 12:54:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-13 12:54:06       18 阅读

热门阅读

  1. 关于初级嵌入式软件工程师应有的思考

    2024-01-13 12:54:06       33 阅读
  2. 如何改造现有文件为 CMD 模块

    2024-01-13 12:54:06       31 阅读
  3. 关于游戏工业化的小讨论

    2024-01-13 12:54:06       36 阅读
  4. [libjsoncpp] libjsoncpp demo

    2024-01-13 12:54:06       29 阅读
  5. 测试人员必备基本功(2)

    2024-01-13 12:54:06       36 阅读
  6. 【代码随想录】刷题笔记Day51

    2024-01-13 12:54:06       33 阅读
  7. google drive api

    2024-01-13 12:54:06       36 阅读
  8. 【AI】Pytorch 系列:学习率设置

    2024-01-13 12:54:06       36 阅读
  9. 网络视频监控和流媒体技术-基础知识整理

    2024-01-13 12:54:06       22 阅读
  10. vue3+TS使用component 组件的实例

    2024-01-13 12:54:06       29 阅读
  11. 多线程面试题目(1)

    2024-01-13 12:54:06       33 阅读
  12. K8S的搭建步骤

    2024-01-13 12:54:06       37 阅读
  13. 单片机学习记录(一)

    2024-01-13 12:54:06       30 阅读
  14. Spring整理-Spring Bean的作用域

    2024-01-13 12:54:06       32 阅读