C語言續(xù)

  • 指針

    指針的定義
    指針的類型
    指針的指向內(nèi)容
    指針的運(yùn)算
    數(shù)組與指針
    指針與函數(shù)
  • 動(dòng)態(tài)分配內(nèi)存

  • 結(jié)構(gòu)體

  • 文件讀寫

  • 頭文件與實(shí)現(xiàn)文件實(shí)例之計(jì)算器

  • 文件操作訓(xùn)練之字符串查找

指針

指針的定義

  • 指針是一個(gè)變量
  • 指針只能存地址
  • 指針占據(jù)8個(gè)字節(jié)空間

總結(jié):指針是一種保存變量地址的變量

int main(){
  int *a;
  char *b;
 printf("a的大?。?d\n", sizeof(a));
 printf("a的地址:%p\n",a);
 printf("%d\n", sizeof(b));
}

輸出:
     a的大小:8
     a的地址:0000000000000001
     8
  • 指針的聲明
int *p;//聲明一個(gè)int類型的指針p
char *p;//聲明一個(gè)char類型的指針p
int *arr[5];//聲明一個(gè)指針數(shù)組,數(shù)組內(nèi)5個(gè)元素,每個(gè)元素都是一個(gè)指向 int 類型對(duì)象的指針
int **p;//聲明一個(gè)指針p,指針指向一個(gè)int類型的指針
  • 指針的聲明相對(duì)于普通變量的聲明多了一個(gè)一元運(yùn)算符 “*”。
  • 運(yùn)算符 “*” 是間接尋址或者間接引用運(yùn)算符。當(dāng)它作用于指針時(shí),將訪問指針?biāo)赶虻膶?duì)象。
  • p 是一個(gè)指針,保存著一個(gè)地址,該地址指向內(nèi)存中的一個(gè)變量;*p 則會(huì)訪問這個(gè)地址所指向的變量。
  • 聲明一個(gè)指針變量并不會(huì)自動(dòng)分配任何內(nèi)存。
  • 在對(duì)指針進(jìn)行間接訪問之前,指針必須進(jìn)行初始化:或是使他指向現(xiàn)有的內(nèi)存,或者給他動(dòng)態(tài)分配內(nèi)存,否則這個(gè)指針會(huì)變成野指針。
  • 指針初始化
/* 方法1:使指針指向現(xiàn)有的內(nèi)存 */
int a = 5;
int *p = &a; 

*: 定義的時(shí)候表明是一個(gè)指針變量,使用的時(shí)候表示取地址的值
&: 取某一個(gè)變量地址

指針初始化
/* 方法2:動(dòng)態(tài)分配內(nèi)存給指針 */
int *p;
p = (int *)malloc(sizeof(int) * 10);    // malloc 函數(shù)用于動(dòng)態(tài)分配內(nèi)存
free(p);                                  // free 函數(shù)用于釋放一塊已經(jīng)分配的內(nèi)存

指針的初始化實(shí)際上就是給指針一個(gè)合法的地址,讓程序能夠清楚地知道指針的指向,而不至于變?yōu)橐爸羔?/strong>

指針的類型

判斷指針類型的方法:去掉星號(hào)*和變量名就是指針的類型

int p;//P是一個(gè)普通的整型變量*
int *p;//P是一個(gè)返回整型數(shù)據(jù)的指針

P與*結(jié)合,所以說明P 是一個(gè)指針,然后再與int 結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型為int型.

int p[3]; //所以P 是一個(gè)由整型數(shù)據(jù)組成的數(shù)組

P與[]結(jié)合,說明P 是一個(gè)數(shù)組,然后與int 結(jié)合,說明數(shù)組里的元素是整型的.

int *p[3]; //P 是一個(gè)由返回整型數(shù)據(jù)的指針?biāo)M成的數(shù)組

P與[]結(jié)合,因?yàn)槠鋬?yōu)先級(jí)比高,所以P 是一個(gè)數(shù)組,然后再與結(jié)合,說明數(shù)組里的元素是指針類型,然后再與int 結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型是整型的.

int (*p)[3]; //P 是一個(gè)指向由整型數(shù)據(jù)組成的數(shù)組的指針

P與*結(jié)合,說明P 是一個(gè)指針然后再與[]結(jié)合(與"()"這步可以忽略,只是為了改變優(yōu)先級(jí)),說明指針?biāo)赶虻膬?nèi)容是一個(gè)數(shù)組,然后再與int 結(jié)合,說明數(shù)組里的元素是整型的.

int **p;//P是一個(gè)指向整型數(shù)的指針的指針(二級(jí)指針)

P與結(jié)合,說是P 是一個(gè)指針,然后再與結(jié)合,說明指針?biāo)赶虻脑厥侵羔?然后再與int 結(jié)合,說明該指針?biāo)赶虻脑厥钦蛿?shù)據(jù).

int p(int);//P是一個(gè)參數(shù)和返回值都為int的一個(gè)函數(shù)

P與()結(jié)合,說明P 是一個(gè)函數(shù),然后進(jìn)入()里分析,說明該函數(shù)有一個(gè)整型變量的參數(shù),然后再與外面的int 結(jié)合,說明函數(shù)的返回值是一個(gè)整型數(shù)據(jù).

int (*p)(int);//P 是一個(gè)指向有一個(gè)整型參數(shù)且返回類型為整型的函數(shù)的指針

P與指針結(jié)合,說明P 是一個(gè)指針,然后與()結(jié)合,說明指針指向的是一個(gè)函數(shù),然后再與()里的int 結(jié)合,說明函數(shù)有一個(gè)int 型的參數(shù),再與最外層的int 結(jié)合,說明函數(shù)的返回類型是整型.

指針的指向內(nèi)容

指針存儲(chǔ)的內(nèi)容為變量的地址,也就是說指針的是一個(gè)指向作用,指向變量所存儲(chǔ)的內(nèi)容

int main(){
int a = 5;
int *p = &a;
return 0;
}
指針指向

指針的運(yùn)算

  • 指針+(-)整數(shù)

可以對(duì)指針變量 p 進(jìn)行 p++、p--、p + i 等操作,所得結(jié)果也是一個(gè)指針,只是指針?biāo)赶虻膬?nèi)存地址相比于 p 所指的內(nèi)存地址前進(jìn)或者后退了 i (對(duì)應(yīng)指針指向類型對(duì)應(yīng)大小)個(gè)操作數(shù)。

int main(){
   char a = '1';
   char *p = &a;
   printf("p:%p\n",p);
   p++;
   printf("p++之后結(jié)果:%p\n",p);
   p--;
   printf("p--之后結(jié)果:%p\n",p);
   p+=5;
   printf("p+5之后結(jié)果:%p\n",p);
  return 0;
}

輸出:
         p:000000000062FE17
p++之后結(jié)果:000000000062FE18
p--之后結(jié)果:000000000062FE17
p+5之后結(jié)果:000000000062FE1C

p 是一個(gè) char 類型的指針,指向內(nèi)存地址0062FE17處。則 p++ 將指向與 p 相鄰的下一個(gè)內(nèi)存地址,由于 int 型數(shù)據(jù)占 4 個(gè)字節(jié),因此 p++ 所指的內(nèi)存地址為 1000000b。其余類推。不過要注意的是,這種運(yùn)算并不會(huì)改變指針變量 p 自身的地址,只是改變了它所指向的地址

數(shù)組與指針
  • 數(shù)組的內(nèi)存空間:
    數(shù)組

    數(shù)組的數(shù)組名其實(shí)可以看作一個(gè)指針,因?yàn)閿?shù)組名是指向數(shù)組的第一個(gè)元素,上面num數(shù)組指向的也就是第一個(gè)元素1,數(shù)組名本身是沒有占有內(nèi)存的
int array[10]={0,1,2,3,4,5,6,7,8,9},value;  
value=array[0]; //也可寫成:value=*array;  
value=array[3]; //也可寫成:value=*(array+3);  
value=array[4]; //也可寫成:value=*(array+4);

另外一種解釋是將數(shù)組名指向數(shù)組的第0個(gè)單元,那么(array+n)也就是一個(gè)指向數(shù)組里的第n個(gè)單元的指針

int main(){
  int num[9] = {1,2,3,4,5,6,7,8,9};
  int *p = num;
  *p++;
   int a = (*p)++; //2
   int b = *(p++); //3
   printf("%d\n%d\n",a, b);

輸出:
     2
     3

p指向的是數(shù)組的首地址,也就是數(shù)組的第一個(gè)元素,那么p++之后也就是對(duì)指針p前進(jìn)了4(int類型)個(gè)操作數(shù),而數(shù)組是分配了連續(xù)空間,所以相對(duì)地址是加減也就是數(shù)組元素的位置變換

  • 指針數(shù)組

指針數(shù)組, 是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素都是指針

int *data[10]={NULL};//注意,一定要初始化
for(int i = 0; i < 10; ++ i){
   data[i] = (int*)malloc(sizeof(int) * 10);
}
data[1][2] = 1;

對(duì)于上面的定義和初始化, data是指針數(shù)組的名字, 也就是指向指針數(shù)組首元素的指針. (指針的指針). data[i] 是data這一個(gè)數(shù)組的第i個(gè)元素, 也就是一個(gè)指向int的指針
指針可以當(dāng)成數(shù)組來使用, data[i][j]*(data[i]+j)是等價(jià)

經(jīng)過上述代碼創(chuàng)建的一個(gè)指針數(shù)組data的使用和int data[10][10]基本相同, 區(qū)別在于后者保證數(shù)組和數(shù)組之間的內(nèi)存地址是連續(xù)的. data[0][9] 和 data[1][0] 是連續(xù)的, 而如果使用指針數(shù)組方式創(chuàng)建的data, 不能保證 data[0][9] 和 data[1][0] 在內(nèi)存上連續(xù)

  • 數(shù)組指針

數(shù)組指針,是一個(gè)指針,它指向一個(gè)數(shù)組

int (*)data[10] = NULL;//一個(gè)指向長(zhǎng)度為10的int數(shù)組(int [10])的指針
//一般, 我們并不會(huì)使用到數(shù)組指針
//一般使用:
int func(int data[][20]){
}

數(shù)組作為參數(shù)傳入函數(shù)的時(shí)候, 對(duì)于被調(diào)用的函數(shù)參數(shù)就是指針. 因此, 這里參數(shù)是一個(gè)"元素為int[20]"的數(shù)組(數(shù)組的數(shù)組), 因此, 在函數(shù)內(nèi)部, data實(shí)際上就是一個(gè)"指向int[20]"的指針(int(*)[20])

  • 盡量不要對(duì)數(shù)組和指針使用sizeof
  • 當(dāng)且僅當(dāng)如malloc(10*sizeof(int))時(shí)使用sizeof

指針與函數(shù)

  • 函數(shù)指針是指向函數(shù)的指針變量
  • 通常我們說的指針變量是指向一個(gè)整型、字符型或數(shù)組等變量,而函數(shù)指針是指向函數(shù)
  • 函數(shù)指針可以像一般函數(shù)一樣,用于調(diào)用函數(shù)、傳遞參數(shù)
    函數(shù)指針聲明
typedef int (*fun_ptr)(int,int); // 聲明一個(gè)指向同樣參數(shù)、返回值的函數(shù)指針類型

下面實(shí)例聲明了函數(shù)指針變量 p,指向函數(shù) max:

#include <stdio.h>
int max(int x, int y){
   return x > y ? x : y;
}

int main(void){
   /* p 是函數(shù)指針 */
   int (* p)(int, int) = & max; // &可以省略
   int a, b, c, d;
   printf("請(qǐng)輸入三個(gè)數(shù)字:");
   scanf("%d %d %d", & a, & b, & c);
   /* 與直接調(diào)用函數(shù)等價(jià),d = max(max(a, b), c) */
   d = p(p(a, b), c);  
   printf("最大的數(shù)字是: %d\n", d); 
   return 0;
}

輸出
請(qǐng)輸入三個(gè)數(shù)字:1 2 3
最大的數(shù)字是: 3

函數(shù)指針變量可以作為某個(gè)函數(shù)的參數(shù)來使用的,回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。也就是說回調(diào)函數(shù)是由別人的函數(shù)執(zhí)行時(shí)調(diào)用你實(shí)現(xiàn)的函數(shù)


  • 下面事例中 populate_array 函數(shù)定義了三個(gè)參數(shù),其中第三個(gè)參數(shù)是函數(shù)的指針,通過該函數(shù)來設(shè)置數(shù)組的值。
  • 實(shí)例中我們定義了回調(diào)函數(shù) getNextRandomValue,它返回一個(gè)隨機(jī)值,它作為一個(gè)函數(shù)指針傳遞給 populate_array 函數(shù)。
  • populate_array 將調(diào)用 10 次回調(diào)函數(shù),并將回調(diào)函數(shù)的返回值賦值給數(shù)組
#include <stdlib.h>  
#include <stdio.h>

// 回調(diào)函數(shù)
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)){
   for (size_t i=0; i<arraySize; i++)
       array[i] = getNextValue();
}

// 獲取隨機(jī)值
int getNextRandomValue(void){
   return rand();
}

int main(void){
   int myarray[10];
   populate_array(myarray, 10, getNextRandomValue);
   for(int i = 0; i < 10; i++) {
       printf("%d ", myarray[i]);
   }
   printf("\n");
   return 0;
}

輸出:
41 18467 6334 26500 19169 15724 11478 29358 26962 24464

動(dòng)態(tài)分配內(nèi)存

  • 動(dòng)態(tài)分配內(nèi)存的原因

1.存儲(chǔ)的數(shù)據(jù) 需要延長(zhǎng)生命周期
2.一個(gè)指針變量需要存儲(chǔ)數(shù)據(jù),變量本身只能存地址,不能存數(shù)據(jù),需要分配內(nèi)存空間來存儲(chǔ)數(shù)據(jù)

  • C 語言為內(nèi)存的分配和管理提供了幾個(gè)函數(shù)(導(dǎo)入庫為<stdlib.h>)
    提供的函數(shù)
  • 內(nèi)存分配

如果使用指針變量接收數(shù)據(jù),必須先為這個(gè)指針變量分配一片指向的內(nèi)存空間

 char *name ;

用malloc(memory alloc)申請(qǐng)內(nèi)存空間

 name = (char *)malloc(10*sizeof(char));

使用realloc動(dòng)態(tài)改變已經(jīng)分配內(nèi)存的大小

name = (char *)realloc(name, 20*sizeof(char));

使用完畢必須自己手動(dòng)釋放內(nèi)存

 free(name); 

結(jié)構(gòu)體

結(jié)構(gòu)體的優(yōu)勢(shì)

可以存儲(chǔ)多種數(shù)據(jù)數(shù)據(jù)的變量

結(jié)構(gòu)體定義

struct student{//定義一個(gè)學(xué)生結(jié)構(gòu)體
       int age;
       char sex;
       char name[10];
  }:
  • student是結(jié)構(gòu)體名稱
  • int age等是標(biāo)準(zhǔn)的變量定義

結(jié)構(gòu)體定義變量

struct student LiMing;//struct student是一種結(jié)構(gòu)體類型類似于int,float類型等
struct student *p = &LiMing;

結(jié)構(gòu)體的訪問

LiMing.age = 18;
LiMing.sex ='m';
LiMing.name ="李明";
//指針使用->訪問元素
p->age = 29;
p->sex = 'f';

結(jié)構(gòu)體內(nèi)存大小計(jì)算

對(duì)齊方式

  • 內(nèi)存小的數(shù)據(jù)類型向內(nèi)存大的數(shù)據(jù)類型對(duì)齊
int main(){
struct A{
       char a;
       int b;
   };
struct B{
       double a;
       int b;
       char c;
};
struct Person{
       char *name;        
       double score;
       int age;
   };
struct Student{
       char name[10];
       int age;
       double score;
   };
printf("%d   %d\n",sizeof(struct A),sizeof(struct B));
printf("%d   %d\n",sizeof(struct Person),sizeof(struct Student));
return 0;
}

輸出:
 8   16
 24  24
  • 在結(jié)構(gòu)體A當(dāng)中,char類型向int類型靠齊
    結(jié)構(gòu)體A
  • 在結(jié)構(gòu)體B當(dāng)中,char ,int類型為double類型靠齊,由上自下的補(bǔ)齊,因?yàn)閕nt類型占8位之后任有4位空著,這時(shí)候char會(huì)類型會(huì)自動(dòng)補(bǔ)齊,占據(jù)剩下的4位
    結(jié)構(gòu)體B
  • 在結(jié)構(gòu)體Person當(dāng)中,字符型指針和double相同大小,int類型向double靠齊,自上而下,沒有空位讓int類型補(bǔ)齊
    結(jié)構(gòu)體Person
  • 在結(jié)構(gòu)體Student當(dāng)中,int類型和char類型向double靠齊,int類型分配8個(gè)字節(jié),但前4位空著,char類型數(shù)組最后兩位補(bǔ)齊,剩余兩個(gè)空位
    結(jié)構(gòu)體Student

文件讀寫

  • 打開文件
//fopen函數(shù)
FILE *fopen( const char * filename, const char * mode );
//fopen函數(shù)使用
FILE *fp = fopen("/Users/pengxiaodong/Desktop/test.txt", "r");
mode的值

  • 寫入文件
//fputc函數(shù)
int fputc( int c, FILE *fp );
//函數(shù)使用
fputc('a', fp);
//fputs函數(shù)
int fputs( const char *s, FILE *fp );//按照一定的格式寫入內(nèi)容
//函數(shù)使用
fputs("jack", fp);

  • 讀取文件
//fgetc函數(shù)
int fgetc( FILE * fp );
//函數(shù)使用
fgetc(fp);

  • 關(guān)閉文件
//關(guān)閉文件
int fclose( FILE *fp );
//fclose函數(shù)使用
fclose(fp);

頭文件與實(shí)現(xiàn)文件實(shí)例之計(jì)算器

  • 計(jì)算器的頭文件Calculator.h
#include <stdio.h>

//頭文件里面聲明函數(shù)
//加法 
int add(int a, int b);
//減法 
int minus(int a, int b);
//乘法 
int multiply(int a, int b);
//除法 
int devide(int a, int b);
  • 計(jì)算器的實(shí)現(xiàn)函數(shù)Calculator.cpp
//實(shí)現(xiàn)文件
//1. 先導(dǎo)入需要實(shí)現(xiàn)的頭文件
//2. 實(shí)現(xiàn)這個(gè)頭文件里面的所有方法 
#include "Calculator.h"

//加法 
i>nt add(int a, int b){
  return a + b; 
} 
//減法 
int minus(int a, int b){
  return a - b;
} 
//乘法 
int multiply(int a, int b){
  return a * b;
} 
//除法 
int devide(int a, int b){
  if (b == 0){
      return 0;
  }else{
      return a / b;
  }
} 
  • 計(jì)算器main函數(shù)入口
#include <stdio.h>

//1.程序的入口函數(shù)
//main.cpp 為了讓閱讀者 
//知道我這里面寫的是入口函數(shù)

//2. 將不同的功能模塊用不用的.h .cpp來封裝
//.h 頭文件 函數(shù)聲明 (不能實(shí)現(xiàn))
//.cpp .c 實(shí)現(xiàn)文件 函數(shù)的具體實(shí)現(xiàn){} 

//3.導(dǎo)入頭文件進(jìn)行使用 
#include <stdio.h>

//頭文件里面聲明函數(shù)
//加法 
int add(int a, int b);
//減法 
int minus(int a, int b);
//乘法 
int multiply(int a, int b);
//除法 
int devide(int a, int b);
/*
1.預(yù)編譯 
*/ 
int main(){
  int result = add(1,1);
  printf("1 + 1 = %d\n", result);
  printf("1 + 1 = %d\n", add(1,1));
  printf("2 - 1 = %d\n", minus(2,1));
  printf("2 * 2 = %d\n", multiply(2,2));
  printf("2 / 2 = %d\n", devide(2,2));
  return 0;
}

void test(){
  
}


文件操作訓(xùn)練之字符串查找

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

//從終端接收字符串 返回這個(gè)字符串的首地址
char *inputName(){
   //1.定義一個(gè)指針變量 指向字符串的首地址
   char *pName = NULL;
   //2.接收輸入
   int i = 0;
   //3.提示操作
   printf("請(qǐng)輸入人名:");
   while (1) {
       //接收一個(gè)字符
       char c = getchar();
       //判斷這個(gè)字符是不是\n
       if (c == '\n') {
           //輸入結(jié)束
           break;
       }
       //判斷是不是第一個(gè)字符
       if(i == 0){
           //使用malloc分配內(nèi)存
           pName = (char *)malloc(1*sizeof(char));
           //判斷是否分配成功
           if(pName == NULL){
               exit(EXIT_FAILURE);
           }
           pName[0] = c;
       }else{
           //使用realloc在之前的基礎(chǔ)上加一個(gè)
           pName = realloc(pName, (i+1)*sizeof(char));
           //判斷是否分配成功
           if(pName == NULL){
               exit(EXIT_FAILURE);
           }
           pName[i] = c;
       }
       
       i++;
   }
   //將當(dāng)前的字符串首地址返回
   return pName;
}

//是否繼續(xù)
bool isContinue(){
   printf("是否繼續(xù)(y/n)?:");
   while (1) {
       char c = getchar();
       getchar();
       if (c == 'y'){
           return true;
       }else if(c == 'n'){
           return false;
       }else{
           printf("輸入格式不對(duì),請(qǐng)重新輸入:");
       }
   }
}
//初始化整個(gè)數(shù)組
char **initNames(int *pNum){
   //1.定義指針變量指向每個(gè)名字的首地址的內(nèi)存
   char **pHead = NULL;
   
   //2.記錄元素個(gè)數(shù)
   int i = 0;
   while (1) {
       //判斷是不是第一個(gè)
       //第一個(gè)使用malloc分配內(nèi)存
       if (i == 0) {
           pHead = malloc(1*sizeof(char *));
           if (pHead == NULL) {
               exit(EXIT_FAILURE);
           }
           
           //輸入人名 將地址放到pHead對(duì)應(yīng)位置
           pHead[0] = inputName();
       }else{
           //使用realloc重新再增加一個(gè)元素
           pHead = realloc(pHead, (i+1)*sizeof(char *));
           if (pHead == NULL) {
               exit(EXIT_FAILURE);
           }
           //輸入人名 將地址放到pHead對(duì)應(yīng)位置
           pHead[i] = inputName();
       }
       
       i++;
       
       //是否繼續(xù)
       bool result = isContinue();
       if (result == false) {
           break;
       }
   }
   
   *pNum = i;
   return pHead;
}

void show(char **pHead, int num){
   printf("輸入%d個(gè)名字:\n",num);
   int i; 
   for ( i = 0; i < num; i++) {
       printf("%s\n",pHead[i]);
   }
   printf("\n");
}

int main(int argc, const char * argv[]) {
   char **pHead = NULL;
   int count = 0;
   pHead = initNames(&count);
   show(pHead, count);
   return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容