C語言學習筆記(1)——指針(上)

之前課程都沒有做記錄。發現基本學完就忘。所以現在在博客記錄下學習的過程。以及一些基本概念。

int *p; //定義了一個可以執行int類型地址的指針變量,名字叫p
p=&a ;// 把a的內存地址複製給p
*p=10//通過指針變量間接的訪問a的值,*p代表指針指向變量的值,p代表指向變量的地址
char *p1=&a;//兩個類型不相同的地址 會出現warning 爲了避免可以用強轉(不要這樣做,不兼容)
char *p1=(char*)&a;會導致a的值發生不可預料的變化(強轉只是語法上對了。實質是不對的)
void *p2 ;可以指向任意類型的地址
//一個地址編號對應的是一個BYTE的空間大小
//一個int四個BYTE,佔了4個地址編號
//地址編號在32位系統下,是一個4個字節的無符號整數,在64位系統下是一個8個字節的無符號整數
//程序中不要出現野指針,但可以出現空指針!!!
{
    int *p;//沒有初始化過值的指針,稱爲野指針
    *p=100;
 }
 {
     int *p;
     p=NULL//如果一個指針變量沒有明確的指向一塊內存,那麼就把這個變量指向NULL,這個指針就是空指針
     #define NULL 0 //NULL在C語言是一個宏常量,值爲零
 }
const int a=100;//c語言中的const是有問題的,因爲可以通過指針變量間接的修改const常量的值,所以在c語言中用#define常量的時候更多,在C++中沒有這個問題,const的值不會被改。
const int *p=&a;//p是一個變量,但指向一個常量,p可以指向一個int類型的地址,但不可以用*p的方式修改這個內存的值。
int *const p;//p是一個常量,但指向一個變量或者常量,可以通過這個*p讀寫這個內存的值

{
    int a[10]={1,2,3,4,5,6,7,8,9,0};
    int *p;
    p=a;//這是合法的,數組的名字代表它的首地址
    p[3]=100;//當指針變量指向一個數組的時候,C語言語法規定指針變量名可以當數組名使用
    printf("%lu,%lu\n",sizeof(a),sizeof(p));//這裏就有區別了,前面sizeof(a)是這個數組的長度爲40 後面sizeof(p)在64位系統下爲8
    p=&a[2];//這也是合法的,注意此時p[3]指向了a[5]

}

char[100]="helloworld!"//定義了一個長度爲100個char的數組,同時初始化數組成員變量
const char *s="helloworld!"//定義了一個字符串常量,s指向這個常量的首地址,因爲指向一個常量,爲了嚴謹用const
同時:
a[0]='a'//合法的,因爲數組的所有成員都是變量
s[0]='a'//非法的,因爲s指向的是一個常量 
如果函數參數是一個數組,那麼函數內部是不知道這個數組成員數量的,所以需要額外加一個參數,說明字符數量。如果是一個字符串不用,因爲字符串以/0結尾。

//指針變量可以計算,如果int *類型加一,變化4個整數,如果char *加一,變化1個整數
{
    int a=0;
    int *p=&a;
    printf("%p,%p,%p\n",p,p+1,p+2);//由於爲int 所以輸出的三個地址之間差值爲4
    但是注意這是輸出地址的改變值
    a1[10]={0};
    int *p1=a;
    *p1+=5;
    *p1=1;//這裏a[5]的值變爲5
    //p[3] 和*(p+3)是等價的。
}
{
    int a=0x12345678;
    char *p=(chao *)&a;
    print("%x\n",*p);//此時小端對齊輸出78 大端對齊12
    print("%x\n",*p,p[1]);//小端對齊輸出78 和56
    *p=0;//此時再輸出a的值爲0x12345600

    //c語言中所有的數據類型都可以理解爲一個char的數組,在這裏簡單的理解就是甚至可以使用p[3]=0.此時輸出a的值爲345678這裏理解一下(加深一下char字符串中出現零代表字符串結束的印象,數組出現0不會結束!)。
    char b[20]={0};//還可以使用這種極端的例子
    int *p1=(int *)b;   
}

ip地址其實是個無符號的整數unsigned int
0.0.0.0255.255.255.255正好是四個char
直接用unsigned char *p=(unsigned char *)&a;//這時輸出p[i]會輸出ip地址/逆序的ip地址
把ip地址化爲整數的程序:
{
    char a[]="192.168.1.1";
    unsigned int ip=0;
    unsigned char *p=(unsigned char *)&ip;
    int a1,a2,a3,a4;
    sscanf(a,"%d.%d.%d.%d",&a1,&a2,&a3,&a4);
    p[0]=a4;
    p[1]=a3;
    p[2]=a2;
    p[3]=a1;
    printf("%u\n",ip);
}
對於數組,用數組可以大大簡化:
char a[2][5]={{4,3,2,1,5},{7,15,25,64,74}};
char *p=(char *)a;
int i,j;
for(i=0,i<10;i++)
{
    for(j=1;j<10-i;j++)
    {
        if(p[j]<p[j-1])
        {
            char tmp =p[j];
            p[j]=p[j-1];
            p[j-1]=tmp;
        }
    }
}
for(i=0;i<2;i++)
{
    for(j=0;j<5;j++)
    {
        printf("%d/n",a[i][j]);
    }
}

//指針數組
char *a[10];//定義了一個指針數組,每個成員是char*類型,共十個成員
int *b[10];定義了一個指針數組,每個成員是int*類型,此時sizeof(a),和sizeof(b)在64位系統下都是80!!!
int i=0;
a=&i;//這裏是非法的,因爲a是個數組的名字,數組名不能做左值
b[0]=&i;//這個纔是合法的
sizeof(b[0]) sizeof(*b[0])分別爲8 4因爲前一個爲地址長度,後面爲一個int4
指針數組做main的形參
int main(int argc ,char **args)
//內部使用用*args[i]
//例如 做一個加減乘除的程序 a 5+6 這裏包括程序名共四個字符串,用
int b=atoi(args[1]);
int c=atoi(args[3]);//將5 6轉化爲int 而+號並不是'+'而是"+" 所以用
char *s=args[2]//得到第二個參數,因爲每個參數的類型都是char * 
char c=s[0]//將加號提取出,也就是得到char數組中得到的第一個成員變量的值
char c=args[2][0]//一般不用上面兩行這樣的形式寫,現在這種纔是比較常見的
但是*號時有問題,因爲linux中*是個通配符,所以在輸入時,用/*當做乘就可以解決這個問題。
//二級指針
int a=0;
int *p = &a;
int **pp;
PP=&p;
**pp=10;//次數a變爲10

//函數的參數是指針變量
C語言想通過函數內部修改實參的值,只能給函數傳遞實參的地址來間接修改實參的值,這就是爲什麼scanf裏面要用&a &b等,scanf要改變a的值,只能用傳遞a的地址的方式間接改變a的值

//把數組名做函數參數
此時,數組內的值會改變,這是因爲當一個數組名作爲函數的形參的時候,其實是一個指針變量名
void test(int a[10])
void test(int a[])
void test(int *a)//以上三種是一樣的,所以一般寫成第三種
void test(const int *a)//這樣寫是爲了不讓函數內部修改數組成員的值,但是可以通過強轉用另外一個地址指向*a,
然後通過另外一個地址修改數組成員的值,也就是說C語言中的const是一直不安全的
{   
printf("%lu\n",sizeof(a));//這裏輸出8
a[5]=50;
}
int main()
{
    int a={0};
    printf("%lu/n",sizeof(a));//這裏輸出40
    test(a);//數組內部值改變了
}
可以定義 int *test()//函數的返回類型是指針類型

下面來看看C語言中的幾個庫函數memset memcpy memmove
memset是將指定區域的內存置空 int a[10]={1,2,3,4,5,6,7,8,9,0}; 此時不能用a[10]={0},只能用for循環一個個將a[10]中的值清空,很麻煩,所以就要用到memset 調用memset需要頭文件#include

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