C語言----指針&結構體

指針是什麼?在計算機科學中,指針(Pointer)是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另一個地方的值。由於通過地址能找到所需的變量單元,可以 說,地址指向該變量單元。因此,將地址形象化的稱爲“指針”。意思是通過它能找到以它爲地址 的內存單元。指針 指針是個變量,存放內存單元的地址(編號)。

#include <stdio.h> int main()
{
int a = 10;//在內存中開闢一塊空間
int *p = &a;//這裏我們對變量a,取出它的地址,可以使用&操作符。
//將a的地址存放在p變量中,p就是一個之指針變量。
return 0;
}

指針就是變量,用來存放地址的變量。(存放在指針中的值都被當成地址處理)。
指針是用來存放地址的,地址是唯一標示一塊地址空間的。 指針的大小在32位平臺是4個字節,在64位平臺是8個字節。
指針和指針類型

char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;

這裏可以看到,指針的定義方式是: type + * 。 其實: char* 類型的指針是爲了存放 char 類型變 量的地址。 short* 類型的指針是爲了存放 short 類型變量的地址。 int* 類型的指針是爲了存放 int 類型變量的地址
指針±整數

#include <stdio.h>
int main()
{
int n = 10;
char pc = (char)&n;
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}

指針的類型決定了指針向前或者向後走一步有多大(距離)。
指針的解引用

指針的類型決定了,對指針解引用的時候有多大的權限(能操作幾個字節)。 比如: char* 的指針解引用就只能訪問一個字節,而 int* 的指針的解引用就能訪問四個字節。野指針

概念: 野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)指針變量 在定義時如果未初始化,其值是隨機的,指針變量的值是別的變量的地址,意味着指針指向了一 個地址是不確定的變量,此時去解引用就是去訪問了一個不確定的地址,所以結果是不可知的。

野指針成因

  1. 指針未初始化
    #include <stdio.h> int main()
    {
    int *p;//局部變量指針未初始化,默認爲隨機值
    *p = 20;
    return 0;
    }
  2. 指針越界訪問
    #include <stdio.h> int main()
    {
    int arr[10] = {0}; int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
    {
    //當指針指向的範圍超出數組arr的範圍時,p就是野指針
    *(p++) = i;
    }
    return 0;
    }
  3. 指針指向的空間釋放

如何規避野指針

  1. 指針初始化
  2. 小心指針越界
  3. 指針指向空間釋放即使置NULL
  4. 指針使用之前檢查有效性
    指針運算

指針± 整數
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指針±整數;指針的關係運算
for (vp = &values[0]; vp < &values[N_VALUES]😉
{
*vp++ = 0;
}
指針-指針
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
指針的關係運算
for(vp = &values[N_VALUES]; vp > &values[0]😉
{
*–vp = 0;
}
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp–)
{
*vp = 0;
}
標準規定:

允許指向數組元素的指針與指向數組最後一個元素後面的那個內存位置的指針比較,但是不允許 與指向第一個元素之前的那個內存位置的指針進行比較。指針和數組

可見數組名和數組首元素的地址是一樣的。
結論:數組名錶示的是數組首元素的地址。
既然可以把數組名當成地址存放到一個指針中,我們使用指針來訪問數組就成爲可能
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr; //指針存放數組首元素的地址 int sz = sizeof(arr)/sizeof(arr[0]); for(i=0; i<sz; i++)
{
printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p+i);
}
return 0;
}
所以 p+i 其實計算的是數組 arr 下標爲i的地址。 那我們就可以直接通過指針來訪問數組
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int *p = arr; //指針存放數組首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0;
for (i = 0; i<sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}

二級指針

指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪裏? 這就是 二級指針

對於二級指針的運算有:

*ppa 通過對ppa中的地址進行解引用,這樣找到的是 pa , *ppa 其實訪問的就是 pa .
int b = 20;
*ppa = &b;//等價於pa = &b;

**ppa 先通過 *ppa 找到 pa ,然後對 pa 進行解引用操作: pa ,那找到的是 a .
**ppa = 30;
//等價於
pa = 30;
//等價於a = 30;
指針數組

int* arr3[5];//是什麼?
arr3是一個數組,有五個元素,每個元素是一個整型指針

結構體

結構體類型的聲明
結構體的聲明
結構的基礎知識 結構是一些值的集合,這些值稱爲成員變量。結構的每個成員可以是不同類型的變量。
結構的聲明
struct tag
{
member-list;
}variable-list;
結構成員的類型
結構的成員可以是標量、數組、指針,甚至是其他結構體。

結構體初始化
struct Point
{
int x; int y;
}p1;//聲明類型的同時定義變量
struct Point p2;//定義結構體變量p2

//初始化:定義變量的同時賦初值。
struct Point p3 = {x, y};
struct stu//聲明類型
{
char name[15];//名字
int age; //年齡
};
struct Stu s = {“zhangsan”, 20};//初始化
struct Node
{
int data; struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //結構體嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//結構體嵌套初始化

結構體成員訪問
結構體變量訪問成員 結構變量的成員是通過點操作符(.)訪問的。點操作符接受兩個操作數。

我們可以看到 s 有成員 name 和 age ; 那我們如何訪問s的成員?
struct S s;
strcpy(s.name, “zhangsan”);//使用.訪問name成員 s.age = 20;//使用.訪問age成員

結構體指針訪問指向變量的成員 有時候我們得到的不是一個結構體變量,而是指向一個結構體的 指針。
那該如何訪問成員。 如下:
struct Stu
{
char name[20]; int age;
};
void print(struct Stu* ps)
{
printf(“name = %s age = %d\n”, (*ps).name, (*ps).age);
//使用結構體指針訪問指向對象的成員
printf(“name = %s age = %d\n”, ps->name, ps->age);
}
int main()
{
struct Stu s = {“zhangsan”, 20};
print(&s);//結構體地址傳參
return 0;
}

結構體傳參

函數傳參的時候,參數是需要壓棧的。 如果傳遞一個結構體對象的時候,結構體過大,參數壓棧 的的系統開銷比較大,所以會導致性能的下降。
結論: 結構體傳參的時候,要傳結構體的地址。

struct S
{
int data[1000]; int num;
};
struct S s = {{1,2,3,4}, 1000};
//結構體傳參
void print1(struct S s)
{
printf("%d\n", s.num);
}
//結構體地址傳參
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //傳結構體 print2(&s); //傳地址 return 0;
}
首選print2函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章