C语言进阶知识 - 指针,结构体,文件读写操作,动态内存分配

本文目录

  • 一、指针
    1.指针的定义
    2.指针的声明
    3.指针初始化
    4.指针的类型
    5.指向指针的内容
    6.指针的运算
  • 二、指针与数组
  • 三、指针与函数
  • 四、动态内存分配
  • 五、文件读写操作
  • 六、结构体

一、指针

1.指针的定义

  • 指针是一个变量
  • 指针只能存地址
  • 指针占据8个字

总结:指针是一种保存变量地址的变量

int main()
{
    int *a;
    char *b;
    printf("a的大小:%d\n,sizeof(a));
    printf("a的地址:%p\n,a);
}
输出结果:
        a的大小:8;
        a的地址:0x100;

2.指针的声明
以下为几个指针声明的例子

int *p; //指向整型变量的指针变量,用来存放整型变量的地址
int a[5]; int *q = a; //指向数组第0个元素的指针变量
//分析:int a[5],有五个元素,分别是变量 a[0]、 a[1]、 a[2]、 a[3]、 a[4],且a代表的是第0个元素(变量a[0])的起始地址。而变量a[0]的类型是int,所以int *p =a; 是合法的。
char *string = "hello"; //指向字符串的指针变量,string 是一个指针变量,用来存放字符串的首地址,而"hello"返回字符串的首地址
int (*foo)(int x, int y); //指向函数的指针变量 函数指针
//分析:
//int (*foo)(int x, int y) 中foo是一个指针变量,那又是怎样的指针变量呢?再看接下来加粗部分:
//int (*foo)(int x, int y); 这说明foo是一个指向函数(该函数有两个int类型的形参,且返回int 类型的数据)的指针变量
struct student *boy; //指向结构体的指针变量
  1. 指针的声明相当于普通变量的声明多了一个一元运算符“ * ”。
  2. 运算符“ * ”是间接寻找地址或者间接引用运算符。当它作用于指针时,将访问指针所指向的对象。
  3. p 是一个指针,保存着一个地址,该地址指向内存中的一个变量;*p则会访问这个地址所指向的变量。
  4. 声明一个指针变量并不会自动分配任何内存
  5. 在对指针进行简介访问之前,指针必须进行初始话:或者使他指向现有的内存,或者给他动态分配内存,否则这个指针会变成野指针(指向未知地址)

3. 指针初始化

/* 方法1:使指针指向现有的内存 */
int x = 1;
int *p = &x;  // 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址

/* 方法2:动态分配内存给指针 */
int *p;
p = (int *)malloc(sizeof(int) * 10);    // malloc 函数用于动态分配内存
free(p);    // free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用,要使用这两个函数需要头文件 stdlib.h

4.指针的类型

  • 去掉星号* 和变量名 就是指针的类型

  • int *p ; char p ; int (func)(int );

  • 指针变量只能指向同种数据类型的地址

  • NULL 空指针,表示不指向任何东西。可以通过给一个指针赋一个零值来生成一个 NULL 指针

  • 常量指针: 可以改变指针的指向
    int a = 20;
    int const *pa = &a; const -> 不变的量
    pa = &b;

  • 指针常量: 可以改变指针指向的变量的值
    int b = 30;
    int * const pb = &b;
    *pb = 50;

  • 指向常量的常量指针:啥都不能变 就是被定义的一个常量
    int const * const pc = &a;

5.指针指向的内容

  • 指针存储的内容为变量的地址,也就是说指针的是一个指向作用,只想变量所存储的内容
  • 通过 *指针名 来访问指针指向地址里面的内容
int main()
{
    int a = 5;
    int *p = &a;
    printf("%d",*p);
    //输出 5 ;
}

6.指针的运算

C 指针的算术运算只限于两种形式:

  • 指针 +/- 整数 :
    可以对指针变量 p 进行 p++、p--、p + i 等操作,所得结果也是一个指针,只是指针所指向的内存地址相比于 p 所指的内存地址前进或者后退了 i 个操作数。用一张图来说明一下:


    指针加减运算.png

    在上图中,10000000等是内存地址的十六进制表示(数值是假定的),p 是一个 int 类型的指针,指向内存地址 0x10000008 处。则 p++ 将指向与 p 相邻的下一个内存地址,由于 int 型数据占 4 个字节,因此 p++ 所指的内存地址为 1000000b。其余类推。不过要注意的是,这种运算并不会改变指针变量 p 自身的地址,只是改变了它所指向的地址。

  • 指针 - 指针
    只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针。两个指针相减的结果的类型是 ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位),因为减法运算的结果将除以数组元素类型的长度。举个例子:

#include "stdio.h"

int main(){
    int a[10] = {1,2,3,4,5,6,7,8,9,0};
    int sub;
    int *p1 = &a[2];
    int *p2 = &a[8];

    sub = p2-p1;                                                                            
    printf("%d\n",sub);    // 输出结果为 6

    return 0;
}

二、指针与数组

  • 数组指针(也称行指针)
    定义 int (p)[n];
    ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
    如要将二维数组赋给一指针,应这样赋值:
    int a[3][4];
    int (
    p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
    p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
    p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
    所以数组指针也称指向一维数组的指针,亦称行指针。

  • 指针数组
    是数组,每一个元素都是指针
    定义 int p[n];
    []优先级高,先与p结合成为一个数组,再由int
    说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。
    如要将二维数组赋给一指针数组:
    int *p[3];
    int a[3][4];
    p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
    for(i=0;i<3;i++)
    p[i]=a[i]
    这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
    所以要分别赋值。

  • 这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
    还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
    比如要表示数组中i行j列一个元素:
    (p[i]+j)、((p+i)+j)、((p+i))[j]、p[i][j]

  • 优先级:()>[]>*


三、指针与函数

(一) 指针函数

  • 指针函数是一个函数,只不过指针函数返回的类型是某一类型的指针。
  • 格式:类型名* 函数名(函数参数列表)
  • 实际运用:
/*
* 指针函数,返回int* 指针变量
*/
int* add(int a, int b)
{
 int *p;
 int c = a + b;
 p = &c;
 return p;
}
int main()
{
 int* p;
 p = add(1, 4);
 printf("%d\n", *p);
 getchar();
 return 1;
}

(二)函数指针

  • 函数指针是指向函数的指针变量。所以函数指针其实是指针变量,只不过该指针变量指向函数。
  • 格式:类型名 (*指针变量名) (函数参数列表)
  • 函数指针的用途:
    a.调用函数
    b.做函数的参数
  • 调用函数:
/*
* 函数指针,调用函数
*/
int fun1(int a, int b)
{
 return a + b;
}
int main()
{
 int(*p)(int, int);
 p = fun1;
 printf("%d\n", p(1, 5));
 getchar();
 return 0;
}
  • 做函数的参数:
/*
* 函数指针,最为参数
*/
int fun2(int a, int b)
{
 return a + b;
}
int fun3(int a, int b, int(*f)(int, int))
{
 return f(a, b);
}
int main()
{
 printf("%d\n", fun3(1, 5, fun2));
 getchar();
 return 1;
}

四、动态内存分配

  • 为什么需要动态内存分配:
    1.存储的数据 需要延长生命周期
    2.一个指针变量需要存储数据,变量本身只能存地址,不能存数据,需要分配内存空间来存储数据

  • 如何分配:
    malloc 申请内存 realloc 重新申请内存
    name = (char)malloc(name,20sizeof(char));

int main()
{
    //如果使用指针变量接收数据
    //必须先为这个指针变量分配一片指向的内存空间
    //有内存空间 才能存数据
    //导入头文件stdlib.h 
    //使用 malloc (memory allloc)申请内存空间 
    
    char *name;
    name = (char*)malloc(10*sizeof(char));  //分配内存 
    if(name == NULL){
        // 申请内存失败
        exit(EXIT_FAILURE);
    } 

    scanf("%s",name);
    printf("%s\n",name); 
    
    //使用realloc动态改变内存的大小——增加或减少
    name = (char*)realloc(name,20*sizeof(char)); 
    if(name == NULL){
        // 申请内存失败 退出之前必须将之前分配的内存释放掉 
        free(name);
        exit(EXIT_FAILURE);
    } 
    free(name);
    return 0;
}

五、文件读写操作**

  • 打开文件

c语言使用fopen()函数来打开一个已存在的文件或者创建一个新的文件;

函数调用原型:

FILE *fopen( const char * filename, const char * mode );

其中,filename是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
有关文件.png
  • 关闭文件

c语言使用fclose()函数来关闭文件;

函数调用原型为:

int fclose( FILE *fp );

文件关闭成功,函数返回0,关闭失败返回EOF。(EOF为定义在<stdio.h>中的常量)

  • 写入文件

c语言使用fputc()函数将字符写入流;

函数原型为:

int fputc( int c, FILE *fp );

如果是字符串则用:

int fputs( const char *s, FILE *fp );

写入成功返回一个非负值,失败返回EOF。

使用 int fprintf(FILE *fp,const char *format, ...) 函数来写把一个字符串写入到文件中。

  • 读取文件

c语言使用fgetc()函数将字符写入流;

函数原型为:

int fgetc( FILE * fp );

如果是字符串则用:

char *fgets( char *buf, int n, FILE *fp );

函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。

如果这个函数在读取最后一个字符之前就遇到一个换行符 '\n' 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。

使用int fscanf(FILE *fp, const char *format, ...) 函数来从文件中读取字符串,但是在遇到第一个空格字符时,它会停止读取。
————————————————。
出自:https://blog.csdn.net/zztingfeng/article/details/82287974


六、结构体

  • 结构体:
    int float 只能存单个数据
    int num[] 可以存多个数据 必须同种类型
    学生:学号long long 年龄 int 姓名 char*

  • 为什么使用结构体:
    需要一种类型,这种类型可以保存多种数据

  • 结构体是什么:
    一种类型

  • 如何定义结构体类型:
    注意定义结构体类型 内部类型不能赋储至
    struct student
    {
    int age;
    char sex;
    char name[10];
    }

  • 如何使用:
    int a = 10;
    struct student xw;
    struct student lw = {40,'m',"隔壁老王"};

  • 使用结构体变量:
    lw.age = 35;

int main()
{   //定义结构体类型 
    struct person{  
        int age;
        float height;
    };
    //定义变量
    struct person xw;
    xw = {33,88.8};
    struct person *p;
    p = &xw;
    cout<<xw.age<<endl; 
    
    //指针使用->访问元素
    p->age = 30;
    cout<<xw.age<<endl;
    
    //将结构体保存到文件
    //打开文件
    FILE *fp = fopen("C:/Users/53573/Desktop/test1.txt","a+");
    //将小王写入文件
    //fwrite(&xw,sizeof(struct person),1,fp); 
    //读取内容
    struct person zs;
    fread(&zs,sizeof(struct person),1,fp);
    struct person *p1 = &zs;
    p1->height = 180.88;
    printf("age:%d height:%f\n",zs.age,zs.height); 
     
    return 0; 
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容