C/C++指標總結
基礎部分
關於記憶體
記憶體含義:
- 儲存器:計算機的組成中,用來儲存程式和資料,輔助CPU進行運算處理的重要部分。
- 記憶體:內部存貯器,暫存程式/資料——掉電丟失 SRAM、DRAM、DDR、DDR2、DDR3。
- 外存:外部儲存器,長時間儲存程式/資料—掉電不丟ROM、ERRROM、FLASH(NAND、NOR)、硬碟、光碟。
記憶體是溝通CPU與硬碟的橋樑:
- 暫存放CPU中的運算資料
- 暫存與硬碟等外部儲存器交換的資料
物理儲存器和儲存地址空間
有關記憶體的兩個概念:物理儲存器和儲存地址空間。
物理儲存器:實際存在的具體儲存器晶片。
- 主機板上裝插的記憶體條
- 顯示卡上的顯示RAM晶片
- 各種適配卡上的RAM晶片和ROM晶片
儲存地址空間:對儲存器編碼的範圍。我們在軟體上常說的記憶體是指這一層含義。
編碼:對每個物理儲存單元(一個位元組)分配一個號碼
定址:可以根據分配的號碼找到相應的儲存單元,完成資料的讀寫
關於記憶體地址
記憶體地址
- 將記憶體抽象成一個很大的一維字元陣列。
- 編碼就是對記憶體的每一個位元組分配一個32位或64位的編號(與32位或者64位處理器相關)。
- 這個記憶體編號我們稱之為記憶體地址。
記憶體中的每一個資料都會分配相應的地址:
- char:佔一個位元組分配一個地址
- int: 佔四個位元組分配四個地址
- float、struct、函式、陣列等
指標和指標變數
指標:
- 記憶體區的每一個位元組都有一個編號,這就是“地址”。
- 如果在程式中定義了一個變數,在對程式進行編譯或執行時,系統就會給這個變數分配記憶體單元,並確定它的記憶體地址(編號)
- 指標的實質就是記憶體“地址”。指標就是地址,地址就是指標。
- 指標是記憶體單元的編號,指標變數是存放地址的變數。
- 通常我們敘述時會把指標變數簡稱為指標,實際他們含義並不一樣。
指標變數的定義和使用
- 指標也是一種資料型別,指標變數也是一種變數
- 指標變數指向誰,就把誰的地址賦值給指標變數
- “*”運算子操作的是指標變數指向的記憶體空間
#include <stdio.h>
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;
}
透過指標間接修改變數的值
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);
指標大小
- 使用sizeof()測量指標的大小,得到的總是:4或8
- sizeof()測的是指標變數指向儲存地址的大小
- 在32位平臺,所有的指標(地址)都是32位(4位元組)
- 在64位平臺,所有的指標(地址)都是64位(8位元組)
野指標和空指標
指標變數也是變數,是變數就可以任意賦值,不要越界即可(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賦值給此指標,這樣就標誌此指標為空指標,沒有任何指標。
int *p = NULL;
NULL是一個值為0的宏常量:
#define NULL ((void *)0)
萬能指標void
void *指標可以指向任意變數的記憶體空間:
void *p = NULL;
int a = 10;
p = (void *)&a; //指向變數時,最好轉換為void *
//使用指標變數指向的記憶體時,轉換為int *
*( (int *)p ) = 11;
printf("a = %d\n", a);
const修飾的指標變數
int a = 100;
int b = 200;
//指向常量的指標
//修飾*,指標指向記憶體區域不能修改,指標指向可以變
const int *p1 = &a; //等價於int const *p1 = &a;
//*p1 = 111; //err
p1 = &b; //ok
//指標常量
//修飾p1,指標指向不能變,指標指向的記憶體可以修改
int * const p2 = &a;
//p2 = &b; //err
*p2 = 222; //ok
指標和陣列
陣列名
陣列名字是陣列的首元素地址,但它是一個常量:
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, 陣列名只是常量,不能修改
指標運算元組元素
#include <stdio.h>
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的地址
for (i = 0; i < n; i++)
{
p[i] = 2 * i;
}
for (i = 0; i < n; i++)
{
printf("%d, ", *(p + i));
}
printf("\n");
return 0;
}
指標加減運算
1)加法運算
- 指標計算不是簡單的整數相加
- 如果是一個int *,+1的結果是增加一個int的大小
- 如果是一個char *,+1的結果是增加一個char大小
#include <stdio.h>
int main()
{
int a;
int *p = &a;
printf("%d\n", p);
p += 2;//移動了2個int
printf("%d\n", p);
char b = 0;
char *p1 = &b;
printf("%d\n", p1);
p1 += 2;//移動了2個char
printf("%d\n", p1);
return 0;
}
透過改變指標指向運算元組元素:
#include <stdio.h>
int main()
{
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++;
}
printf("\n");
return 0;
}
減法運算
示例1:
#include <stdio.h>
int main()
{
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--;
}
printf("\n");
return 0;
}
示例2:
#include <stdio.h>
int main()
{
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 = 1
int n2 = (int)p2 - (int)p1; //n2 = 4
printf("n1 = %d, n2 = %d\n", n1, n2);
return 0;
}
指標陣列
指標陣列,它是陣列,陣列的每個元素都是指標型別。
#include <stdio.h>
int main()
{
//指標陣列
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]));
}
printf("\n");
return 0;
}
多級指標
C語言允許有多級指標存在,在實際的程式中一級指標最常用,其次是二級指標。
二級指標就是指向一個一級指標變數地址的指標,三級指標基本用不著。
int a = 10;
int *p = &a; //一級指標
*p = 100; //*p就是a
int **q = &p;
//*q就是p
//**q就是a
int ***t = &q;
//*t就是q
//**t就是p
//***t就是a
指標和函式
函式形參改變實參的值
#include <stdio.h>
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;
}
陣列名做函式引數
陣列名做函式引數,函式的形參會退化為指標:
#include <stdio.h>
//void printArrary(int a[10], int n)
//void printArrary(int a[], int n)
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;
}
指標做為函式的返回值
#include <stdio.h>
int a = 10;
int *getA()
{
return &a;
}
int main()
{
*( getA() ) = 111;
printf("a = %d\n", a);
return 0;
}
指標和字串
字元指標
#include <stdio.h>
int main()
{
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);
return 0;
}
字元指標做函式引數
#include <stdio.h>
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;
}
const修飾的指標變數
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
//const修飾一個變數為只讀
const int a = 10;
//a = 100; //err
//指標變數, 指標指向的記憶體, 2個不同概念
char buf[] = "aklgjdlsgjlkds";
//從左往右看,跳過型別,看修飾哪個字元
//如果是*, 說明指標指向的記憶體不能改變
//如果是指標變數,說明指標的指向不能改變,指標的值不能修改
const char *p = buf;
// 等價於上面 char const *p1 = buf;
//p[1] = '2'; //err
p = "agdlsjaglkdsajgl"; //ok
char * const p2 = buf;
p2[1] = '3';
//p2 = "salkjgldsjaglk"; //err
//p3為只讀,指向不能變,指向的記憶體也不能變
const char * const p3 = buf;
return 0;
}
指標陣列做為main函式的形參
int main(int argc, char *argv[]);
main函式是作業系統呼叫的,第一個引數標明argv陣列的成員數量,argv陣列的每個成員都是char *型別
argv是命令列引數的字串陣列
argc代表命令列引數的數量,程式名字本身算一個引數
#include <stdio.h>
//argc: 傳引數的個數(包含可執行程式)
//argv:指標陣列,指向輸入的引數
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;
}
常用字串應用模型
1.strstr中的while和do-while模型
利用strstr標準庫函式找出一個字串中substr出現的個數。
a) while模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
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);
return 0;
}
b) do-while模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
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);
return 0;
}
2.兩頭堵模型
求非空字串元素的個數:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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.字串反轉模型(逆置)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}
指標小結
原文連結: https:// blog.csdn.net/qq_423221 03/article/details/99087036
該文章已整理為PDF文件,想要獲取資料的可以私聊我,【關鍵字】+資料
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946034/viewspace-2655604/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C++中const與指標、引用的總結C++指標
- C++ this 指標C++指標
- C++ 指標C++指標
- C++ 指標常見用法小結C++指標
- C++指標理解C++指標
- C++智慧指標C++指標
- C++物件導向總結——虛指標與虛擬函式表C++物件指標函式
- c++ 函式指標C++函式指標
- C++(函式指標)C++函式指標
- C++中的this指標C++指標
- 詳解c++指標的指標和指標的引用C++指標
- 1-7 C++指標C++指標
- C++進階(智慧指標)C++指標
- C++ 類成員指標C++指標
- [C++] 成員函式指標和函式指標C++函式指標
- 【C++系列】指標物件和物件指標的區別C++指標物件
- 【C++】 C++知識點總結C++
- c++ 智慧指標用法詳解C++指標
- 120 C++中的物件指標C++物件指標
- C++筆記(11) 智慧指標C++筆記指標
- C++ const 總結C++
- C++:字串總結C++字串
- C++ 用智慧指標這樣包裝 this 指標是否可行C++指標
- C語言指標總結大學霸IT達人C語言指標
- C++中結構體是使用例項還是指標C++結構體指標
- C++標準庫有四種智慧指標C++指標
- c語言指標彙總C語言指標
- c++ 類的函式引用 指標C++函式指標
- C++基礎總結C++
- C++除錯總結C++除錯
- c++學習總結C++
- iOS野指標定位總結iOS指標
- C/C++高階訓練之指標初識C++指標
- C++定義函式指標,回撥C#C++函式指標C#
- C++ 指標動態記憶體分配C++指標記憶體
- C++ 返回函式指標的函式C++函式指標
- c++指標傳遞與引用傳遞C++指標
- C++ 指標和迭代器支援的操作C++指標