日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C/C++学习之路_六: 指针

發布時間:2024/4/11 c/c++ 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++学习之路_六: 指针 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C/C++學習之路_第六章: 指針


目錄

  • 概述
  • 指針基礎知識
  • 指針和數組
  • 多級指針
  • 指針和函數
  • 指針和字符串
  • 指針小結

  • 1. 概述

    1. 內存

  • 內存含義:

  • 存儲器:計算機的組成中,用來存儲程序和數據,輔助CPU進行運算處理的重要部分。
  • 內存:內部存貯器,暫存程序/數據——掉電丟失。SRAM、DRAM、DDR、DDR2、DDR3。
  • 外存:外部存儲器,長時間保存程序/數據—掉電不丟。ROM、ERRROM、FLASH(NAND、NOR)、硬盤、光盤。
  • 內存是溝通CPU與硬盤的橋梁:

  • 暫存放CPU中的運算數據
  • 暫存與硬盤等外部存儲器交換的數據
  • 2. 物理存儲器和存儲地址空間

  • 有關內存的兩個概念:物理存儲器和存儲地址空間。

  • 物理存儲器:實際存在的具體存儲器芯片。

  • 主板上裝插的內存條
  • 顯示卡上的顯示RAM芯片
  • 各種適配卡上的RAM芯片和ROM芯片
  • 存儲地址空間:對存儲器編碼的范圍,我們在軟件上常說的內存是指這一層含義。

  • 編碼:對每個物理存儲單元(一個字節)分配一個號碼
  • 尋址:可以根據分配的號碼找到相應的存儲單元,完成數據的讀寫
  • 3. 內存地址

  • 將內存抽象成一個很大的一維字符數組。
  • 編碼就是對內存的每一個字節分配一個32位或64位的編號(與32位或者64位處理器相關),這個內存編號我們稱之為內存地址。
  • 內存中的每一個數據都會分配相應的地址:
  • char:占一個字節分配一個地址
  • int: 占四個字節分配四個地址
  • float、struct、函數、數組等
  • 4. 指針和指針變量

  • 內存區的每一個字節都有一個編號,這就是“地址”。
  • 如果在程序中定義了一個變量,在對程序進行編譯或運行時,系統就會給這個變量分配內存單元,并確定它的內存地址(編號)
  • 指針的實質就是內存“地址”。指針就是地址,地址就是指針。
  • 指針是內存單元的編號,指針變量是存放地址的變量。
  • 通常我們敘述時會把指針變量簡稱為指針,實際他們含義并不一樣。

  • 2. 指針基礎知識

    1. 指針變量的定義和使用

  • 指針也是一種數據類型,指針變量也是一種變量
  • 指針變量指向誰,就把誰的地址賦值給指針變量
  • “*”操作符操作的是指針變量指向的內存空間
  • int main() {int a = 0;char b = 100;printf("%p, %p\n", &a, &b); //打印a, b的地址//int *代表是一種數據類型,int*指針類型,p才是變量名//定義了一個指針類型的變量,可以指向一個int類型變量的地址int *p;p = &a;//將a的地址賦值給變量p,p也是一個變量,值是一個內存地址編號printf("%d\n", *p);//p指向了a的地址,*p就是a的值char *p1 = &b;printf("%c\n", *p1);//*p1指向了b的地址,*p1就是b的值return 0; }
  • 注意:&可以取得一個變量在內存中的地址。但是,不能取寄存器變量,因為寄存器變量不在內存里,而在CPU里面,所以是沒有地址的。
  • 2. 通過指針間接修改變量的值

    int a = 0;int b = 11;int *p = &a;*p = 100;printf("a = %d, *p = %d\n", a, *p);p = &b;*p = 22;printf("b = %d, *p = %d\n", b, *p);

    3. 指針大小

  • 使用sizeof()測量指針的大小,得到的總是:4或8
  • sizeof()測的是指針變量指向存儲地址的大小
  • 在32位平臺,所有的指針(地址)都是32位(4字節)
  • 在64位平臺,所有的指針(地址)都是64位(8字節)
  • int *p1;int **p2;char *p3;char **p4;printf("sizeof(p1) = %d\n", sizeof(p1));printf("sizeof(p2) = %d\n", sizeof(p2));printf("sizeof(p3) = %d\n", sizeof(p3));printf("sizeof(p4) = %d\n", sizeof(p4));printf("sizeof(double *) = %d\n", sizeof(double *));

    4. 野指針和空指針

  • 指針變量也是變量,是變量就可以任意賦值,不要越界即可(32位為4字節,64位為8字節),但是,任意數值賦值給指針變量沒有意義,因為這樣的指針就成了野指針,此指針指向的區域是未知(操作系統不允許操作此指針指向的內存區域)。
  • 所以,野指針不會直接引發錯誤,操作野指針指向的內存區域才會出問題。
  • int a = 100;int *p;p = a; //把a的值賦值給指針變量p,p為野指針, ok,不會有問題,但沒有意義p = 0x12345678; //給指針變量p賦值,p為野指針, ok,不會有問題,但沒有意義*p = 1000; //操作野指針指向未知區域,內存出問題,err
  • 但是,野指針和有效指針變量保存的都是數值,為了標志此指針變量沒有指向任何變量(空閑可用),C語言中,可以把NULL賦值給此指針,這樣就標志此指針為空指針,沒有任何指針。
  • NULL是一個值為0的宏常量:
  • #define NULL ((void *)0)int *p = NULL;

    5. 萬能指針void *

  • void *指針可以指向任意變量的內存空間:
  • void *p = NULL;int a = 10;p = (void *)&a; //指向變量時,最好轉換為void *//使用指針變量指向的內存時,轉換為int **( (int *)p ) = 11;printf("a = %d\n", a);

    6. const修飾的指針變量

  • 在編輯程序時,指針作為函數參數,如果不想修改指針對應內存空間的值,需要使用const修飾指針數據類型。
  • int a = 100;int b = 200;//指向常量的指針//修飾*,指針指向內存區域不能修改,指針指向可以變const int * p1 = &a; //等價于int const *p1 = &a;//*p1 = 111; //errp1 = &b; //ok//指針常量//修飾p1,指針指向不能變,指針指向的內存可以修改int * const p2 = &a;//p2 = &b; //err*p2 = 222; //ok

    3. 指針和數組

    1. 數組名

  • 數組名字是數組的首元素地址,但它是一個常量:
  • int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };printf("a = %p\n", a);printf("&a[0] = %p\n", &a[0]);//a = 10; //err, 數組名只是常量,不能修改

    2. 指針操作數組元素

  • 數組名字是數組的首元素地址,即 *a表示首元素1的地址
  • 指針加1,表示跨過一個步長,比如 int* p,步長=sizeof(int)
  • 遍歷時*(a+i)就表示遍歷每個數組元素
  • 指針也能用下標去獲取值,比如*p,通過p[i]獲取下標值
  • int main() {int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};int i = 0;int n = sizeof(a) / sizeof(a[0]);for (i = 0; i < n; i++) { //打印數組元素 // printf("%d, ", a[i]);printf("%d, ", *(a + i));}printf("\n");int *p = a; //定義一個指針變量保存a的地址,指針p保存的是首元素的地址for (i = 0; i < n; i++) {p[i] = 2 * i;}for (i = 0; i < n; i++) {printf("%d, ", *(p + i));}return 0; }

    3. 指針加減運算

    1. 加法運算

  • 指針計算不是簡單的整數相加
  • 如果是一個int *,+1的結果是增加一個int的大小
  • 如果是一個char *,+1的結果是增加一個char大小
  • int a;int *p = &a;printf("%d\n", p);p += 2;//移動了2個intprintf("%d\n", p);char b = 0;char *p1 = &b;printf("%d\n", p1);p1 += 2;//移動了2個charprintf("%d\n", p1);
  • 通過改變指針指向操作數組元素:
  • int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int i = 0;int n = sizeof(a) / sizeof(a[0]);int *p = a;for (i = 0; i < n; i++){printf("%d, ", *p);p++;}

    2. 減法運算

  • 示例1:
  • int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};int i = 0;int n = sizeof(a) / sizeof(a[0]);int *p = a + n - 1;for (i = 0; i < n; i++) {printf("%d, ", *p);p--;}
  • 示例2:
  • int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};int *p2 = &a[2]; //第2個元素地址int *p1 = &a[1]; //第1個元素地址printf("p1 = %p, p2 = %p\n", p1, p2);int n1 = p2 - p1; //n1 = 1int n2 = (int) p2 - (int) p1; //n2 = 4printf("n1 = %d, n2 = %d\n", n1, n2);

    4. 指針數組

  • 指針數組,它是數組,數組的每個元素都是指針類型。
  • //指針數組int *p[3];int a = 1;int b = 2;int c = 3;int i = 0;p[0] = &a;p[1] = &b;p[2] = &c;for (i = 0; i < sizeof(p) / sizeof(p[0]); i++ ){printf("%d, ", *(p[i]));}

    4. 多級指針

  • C語言允許有多級指針存在,在實際的程序中一級指針最常用,其次是二級指針。
  • 二級指針就是指向一個一級指針變量地址的指針。
  • int a = 10;int *p = &a; //一級指針*p = 100; //*p就是aint **q = &p;//*q就是p//**q就是aint ***t = &q;//*t就是q//**t就是p//***t就是a

    5. 指針和函數

    1. 函數形參改變實參的值

    void swap1(int x, int y) {int tmp;tmp = x;x = y;y = tmp;printf("x = %d, y = %d\n", x, y); }void swap2(int *x, int *y) {int tmp;tmp = *x;*x = *y;*y = tmp; }int main() {int a = 3;int b = 5;swap1(a, b); //值傳遞printf("a = %d, b = %d\n", a, b);a = 3;b = 5;swap2(&a, &b); //地址傳遞printf("a2 = %d, b2 = %d\n", a, b);return 0; }

    2. 數組名做函數參數

  • 數組名做函數參數,函數的形參會退化為指針:
  • void printArrary(int *a, int n) {int i = 0;for (i = 0; i < n; i++) {printf("%d, ", a[i]);}printf("\n"); }int main() {int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};int n = sizeof(a) / sizeof(a[0]);//數組名做函數參數printArrary(a, n);return 0; }

    3. 指針做為函數的返回值

    int a = 10;int *getA() {return &a; }int main() {*(getA()) = 111;printf("a = %d\n", a);return 0; }

    6. 指針和字符串

    1. 字符指針

    char str[] = "hello world";char *p = str;*p = 'm';p++;*p = 'i';printf("%s\n", str);p = "mike jiang";printf("%s\n", p);char *q = "test";printf("%s\n", q);

    2. 字符指針做函數參數

    void mystrcat(char *dest, const char *src) {int len1 = 0;int len2 = 0;while (dest[len1]) {len1++;}while (src[len2]) {len2++;}int i;for (i = 0; i < len2; i++) {dest[len1 + i] = src[i];} }int main() {char dst[100] = "hello mike";char src[] = "123456";mystrcat(dst, src);printf("dst = %s\n", dst);return 0; }

    3. const修飾的指針變量

    int main() {//const修飾一個變量為只讀const int a = 10;//a = 100; //err//指針變量, 指針指向的內存, 2個不同概念char buf[] = "aklgjdlsgjlkds";//從左往右看,跳過類型,看修飾哪個字符//如果是*, 說明指針指向的內存不能改變//如果是指針變量,說明指針的指向不能改變,指針的值不能修改const char *p = buf;// 等價于上面 char const *p1 = buf;//p[1] = '2'; //errp = "agdlsjaglkdsajgl"; //okchar *const p2 = buf;p2[1] = '3';//p2 = "salkjgldsjaglk"; //err//p3為只讀,指向不能變,指向的內存也不能變const char *const p3 = buf;return 0; }

    4. 字符指針數組

    char *num[3] = {"heihei", "haha", "xixi"};printf("%c\n", *num[0]); //num[0]表示"heihei"char數組首元素地址,*num[0]='h'printf("%s\n", num[0]); //num[0]表示"heihei"char數組首元素地址,可以輸出"heihei"//num[1]表示"haha"char數組首元素地址,num[1]+1表示a的地址,*(num[1]+1)輸出char'a'printf("%c\n", *(num[1] + 1));//定義一個指針p保存num數組首元素的地址,&num[0] numchar **p = num; //p表示數組首元素地址,num[0],即"heihei"的地址for (int i = 0; i < 3; i++) {//printf("%s\n",*(p+i));printf("%s\n", p[i]); // 打印數組元素}// p+1表示"haha"的地址,即num[1]的地址// *(p + 1)表示num[1]數組首元素地址,即'h',//*(p + 1) + 3表示'h'的地址后移3個的地址,即'a'的地址//*(*(p + 1) + 3)表示用'a'的地址,取char 'a'printf("%c\n", *(*(p + 1) + 3));// *(p[1]+3) == p[1][3]

    5. 指針數組做為main函數的形參

    int main(int argc, char *argv[]);
  • main函數是操作系統調用的,第一個參數標明argc數組的成員數量,argv數組的每個成員都是char *類型
  • argv是命令行參數的字符串數組
  • argc代表命令行參數的數量,程序名字本身算一個參數
  • int main(int argc, char *argv[]) {//指針數組,它是數組,每個元素都是指針char *a[] = {"aaaaaaa", "bbbbbbbbbb", "ccccccc"};int i = 0;printf("argc = %d\n", argc);for (i = 0; i < argc; i++) {printf("%s\n", argv[i]);}return 0; }

    6. 項目開發常用字符串應用

    1. strstr中的while和do-while模型

  • 利用strstr標準庫函數找出一個字符串中substr出現的個數。
  • 1. while模型
    char *p = "11abcd111122abcd333abcd3322abcd3333322qqq";int n = 0;while ((p = strstr(p, "abcd")) != NULL) {//能進來,肯定有匹配的子串//重新設置起點位置p = p + strlen("abcd");n++;if (*p == 0) { //如果到結束符break;}}printf("n = %d\n", n);
    2. do-while模型
    char *p = "11abcd111122abcd333abcd3322abcd3333322qqq";int n = 0;do {p = strstr(p, "abcd");if (p != NULL) {n++; //累計個數//重新設置查找的起點p = p + strlen("abcd");} else { //如果沒有匹配的字符串,跳出循環break;}} while (*p != 0); //如果沒有到結尾printf("n = %d\n", n);

    2. 兩頭堵模型

  • 求非空字符串元素的個數:
  • int fun(char *p, int *n) {if (p == NULL || n == NULL) {return -1;}int begin = 0;int end = strlen(p) - 1;//從左邊開始,如果當前字符為空,而且沒有結束while (p[begin] == ' ' && p[begin] != 0) {begin++; //位置從右移動一位}//從右往左移動while (p[end] == ' ' && end > 0) {end--; //往左移動}if (end == 0) {return -2;}//非空元素個數*n = end - begin + 1;return 0; }int main(void) {char *p = " abcddsgadsgefg ";int ret = 0;int n = 0;ret = fun(p, &n);if (ret != 0) {return ret;}printf("非空字符串元素個數:%d\n", n);return 0; }

    3. 字符串反轉模型(逆置)

    int inverse(char *p) {if (p == NULL) {return -1;}char *str = p;int begin = 0;int end = strlen(str) - 1;char tmp;while (begin < end) {//交換元素tmp = str[begin];str[begin] = str[end];str[end] = tmp;begin++; //往右移動位置end--; //往左移動位置}return 0; }int main(void) {//char *str = "abcdefg"; //文件常量區,內容不允許修改char str[] = "abcdef";int ret = inverse(str);if (ret != 0) {return ret;}printf("str ========== %s\n", str);return 0; }

    7. 字符串處理函數

    1. strcpy()

    #include <string.h> char *strcpy(char *dest, const char *src); 功能:把src所指向的字符串復制到dest所指向的空間中,'\0'也會拷貝過去 參數:dest:目的字符串首地址src:源字符首地址 返回值:成功:返回dest字符串的首地址失敗:NULL
  • 注意:如果參數dest所指的內存空間不夠大,可能會造成緩沖溢出的錯誤情況。
  • char dest[20] = "123456789";char src[] = "hello world";strcpy(dest, src);printf("%s\n", dest);

    2. strncpy()

    #include <string.h> char *strncpy(char *dest, const char *src, size_t n); 功能:把src指向字符串的前n個字符復制到dest所指向的空間中,是否拷貝結束符看指定的長度是否包含'\0'。 參數:dest:目的字符串首地址src:源字符首地址n:指定需要拷貝字符串個數 返回值:成功:返回dest字符串的首地址失敗:NULL

    3. strcat()

    #include <string.h> char *strcat(char *dest, const char *src); 功能:將src字符串連接到dest的尾部,‘\0’也會追加過去 參數:dest:目的字符串首地址src:源字符首地址 返回值:成功:返回dest字符串的首地址失敗:NULLchar str[20] = "123";char *src = "hello world";printf("%s\n", strcat(str, src));

    4. strcmp()

    #include <string.h> int strcmp(const char *s1, const char *s2); 功能:比較 s1 和 s2 的大小,比較的是字符ASCII碼大小。 參數:s1:字符串1首地址s2:字符串2首地址 返回值:相等:0大于:>0 在不同操作系統strcmp結果會不同 返回ASCII差值小于:<0char *str1 = "hello world";char *str2 = "hello mike";if (strcmp(str1, str2) == 0) {printf("str1==str2\n");} else if (strcmp(str1, str2) > 0) {printf("str1>str2\n");} else {printf("str1<str2\n");}

    5. strncmp()

    #include <string.h> int strncmp(const char *s1, const char *s2, size_t n); 功能:比較 s1 和 s2 前n個字符的大小,比較的是字符ASCII碼大小。 參數:s1:字符串1首地址s2:字符串2首地址n:指定比較字符串的數量 返回值:相等:0大于: > 0小于: < 0char *str1 = "hello world";char *str2 = "hello mike";if (strncmp(str1, str2, 5) == 0) {printf("str1==str2\n");} else if (strcmp(str1, "hello world") > 0) {printf("str1>str2\n");} else {printf("str1<str2\n");}

    6. sprintf()

    #include <stdio.h> int sprintf(char *str, const char *format, ...); 功能:根據參數format字符串來轉換并格式化數據,然后將結果輸出到str指定的空間中,直到出現字符串結束符 '\0' 為止。 參數:str:字符串首地址format:字符串格式,用法和printf()一樣 返回值:成功:實際格式化的字符個數失敗: - 1char dst[100] = { 0 };int a = 10;char src[] = "hello world";printf("a = %d, src = %s", a, src);printf("\n");int len = sprintf(dst, "a = %d, src = %s", a, src);printf("dst = \" %s\"\n", dst);printf("len = %d\n", len);

    7. sscanf

    #include <stdio.h> int sscanf(const char *str, const char *format, ...); 功能:從str指定的字符串讀取數據,并根據參數format字符串來轉換并格式化數據。 參數:str:指定的字符串首地址format:字符串格式,用法和scanf()一樣 返回值:成功:參數數目,成功轉換的值的個數失敗: - 1int year =0 ;int month = 0;int day = 0;char buf[1024] = "beijing:2018:t:10:20";//scanf("%d:%d:%d",&year,&month,&day);//從鍵盤按照相應的格式獲取數據sscanf(buf, "beijing:%d:t:%d:%d", &year, &month, &day);//從buf中按照`相應的格式獲取數據printf("%d %d %d\n",year,month,day);

    8. strchr()

    #include <string.h> char *strchr(const char *s, int c); 功能:在字符串s中查找字母c出現的位置 參數:s:字符串首地址c:匹配字母(字符) 返回值:成功:返回第一次出現的c地址失敗:NULLchar src[] = "ddda123abcd";char *p = strchr(src, 'a');printf("p = %s\n", p);

    9. strstr()

    #include <string.h> char *strstr(const char *haystack, const char *needle); 功能:在字符串haystack中查找字符串needle出現的位置 參數:haystack:源字符串首地址needle:匹配字符串首地址 返回值:成功:返回第一次出現的needle地址失敗:NULLchar src[] = "ddddabcd123abcd333abcd";char *p = strstr(src, "abcd");printf("p = %s\n", p);

    10. strtok()

    #include <string.h> char *strtok(char *str, const char *delim); 功能:來將字符串分割成一個個片段。當strtok()在參數s的字符串中發現參數delim中包含的分割字符時, 則會將該字符改為\0 字符,當連續出現多個時只替換第一個為\0。 參數:str:指向欲分割的字符串delim:為分割字符串中包含的所有字符 返回值:成功:分割后字符串首地址失敗:NULL
  • 在第一次調用時:strtok()必需給予參數s字符串
  • 往后的調用則將參數s設置成NULL,每次調用成功則返回指向被分割出片段的指針
  • char str[] = "15080015225&bangquan#82263&123456";char *p[10] = {NULL};//初始化指針數組元素全部為NULLint i = 0;do {if (i == 0)p[i] = strtok(str, "#&");elsep[i] = strtok(NULL, "#&");} while (p[i++] != NULL);//p[i] != NULL i=i+1 如果strtok的返回值等于NULL,代表切割完畢i = 0;while (p[i] != NULL) {printf("%s\n", p[i++]);}

    11. atoi()

    #include <stdlib.h> int atoi(const char *nptr); 功能:atoi()會掃描nptr字符串,跳過前面的空格字符,直到遇到數字或正負號才開始做轉換,而遇到非數字或字符串結束符('\0')才結束轉換,并將結果返回返回值。 參數:nptr:待轉換的字符串 返回值:成功轉換后整數
  • 類似的函數有:
  • atof():把一個小數形式的字符串轉化為一個浮點數。
  • atol():將一個字符串轉化為long類型
  • char str1[] = "-10";int num1 = atoi(str1);printf("num1 = %d\n", num1);char str2[] = "0.123"; double num2 = atof(str2);printf("num2 = %lf\n", num2);

    7. 指針小結

    定義說明
    int i定義整形變量
    int *p定義一個指向int的指針變量
    int a[10]定義一個有10個元素的數組,每個元素類型為int
    int *p[10]定義一個有10個元素的數組,每個元素類型為int*
    int func()定義一個函數,返回值為int型
    int *func()定義一個函數,返回值為int *型
    int **p定義一個指向int的指針的指針,二級指針

    總結

    以上是生活随笔為你收集整理的C/C++学习之路_六: 指针的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。