C語言

  1. 自定義函數
    (1)在函數使用之前定義函數
    (2)先聲明這個函數,然后使用,使用的代碼后面定義這個函數

    include <stdio.h>

    void syahello(){
    println("helloo");
    }

int main(){
sayhello(); // 調用的函數要提前寫出來,如果要寫在后面,需要先在前面聲明
}


void syahello(); //提前聲明,沒有實現
int main(){
sayhello(); // 調用的函數要提前寫出來,如果要寫在后面,需要先在前面聲明
}
void syahello(){
println("helloo");
}


mytest1(); //如果函數沒有明確標明函數的返回值類型,則默認為返回int(不要這樣寫)
print("%d\n",mytest1());

2.形參和實參是值傳遞v,是單向傳遞,只能由實參傳遞給形參,不能由形參傳遞給實參
入股哦函數的參數是數組,則可以通過形參修改實參的值

void testarr(char s[]){
s[0] = 'a';
s[1]='b';
}

int main(){
char chs[100] = "hello world"
testarr(chs);
printf("%d",chs); // 數組變為abllo world
}


void mergearr(char arr1[],char arr2[]){
int len1 = 0;
while(arr1[len1++]);
len1--; // 記錄數組一的長度

int len2=0;
while(arrp[len2+1]);
len2--;int i;
for(i=0;i<len2;i++){arr1[len1+i] = arr2[i];
}

}

int main(){
char arr1[100]="asdad";
char arr2[100]="1111";
mergearr(arr1,arr2);
printf("%s\n",arr1); // 發現數組2已經合并了
printf("%s\n",arr2);
}

二. 多文件編譯

  1. <stdio.h> : 尖括號表示文件在系統路徑中
    "abc/myhead.h":表示abc目錄下的myhead.h
    -------------------------
    myhead.h:
    #define MAX 300 // #define就是簡單的字符串替換

main.c:

include <stdio.h>

include "abc/myhead.h"

int main(){
int c = MAX;
printf("%d",c)
}

三. 指針
計算機內存的最小單位使BYTE
每個BYTE的內存都有一個唯一的內存編號,這個編號就是內存地址
編號在32為系統下是一個32位的整數,編號在64位系統下是一個64位的整數

  1. 用指針型變量來表示這個內存地址號。
  2. :指針變量指向的內容。指針型變量不能向int變量一樣顯示的聲明出來,只能用變量名來聲明一個int值,則該變量是指針型變量,*變量名是一個int值
  3. &:得到變量對應的指針變量(內存地址)

void main{
int a=3;
int p; // p是int型變量,p是指針型變量
p = &a; // 指針型變量就是內存號,所以&符號取得a的內存號
int b = p;
printf("%d",b);
p = 100;
printf("%d",a) // 通過內存號修改變量值

char chs[10];
printf("%u,%u,%u,%u",chs,chs[0],chs[1],chs[2])  // %u:unsigned無符號整形,數組首地址,第一個元素的地址,第二個元素的地址int ints[10];
printf("%u,%u,%u,%u",ints,ints[0],ints[1],ints[2])  // %u:unsigned無符號整形,數組首地址,第一個元素

}

  1. 無類型指針:
    void *p:說明p只是一個指針比那輛,而不去指向任何一個變量
    -----------------------------------

    include <stdio.h>

    void main(){
    int a = 100;
    int p; // int p是聲明寫法:整體代表一個地址號,這個地址號上存著整數。單獨一個p:定義寫法,也是內存號
    int q;
    p = &a;
    //int
    p = 1; // p指向的地址號是1(聲明+定義寫在了一起)
    printf("%u",p); // 3852910012
    printf("%u",*p); // 100
    printf("%d\n",sizeof(p)); //8:指針在64位系統下就是一個64位的整數,所以占8字節。無論指針指向什么類型的數據,64位下都占8字節
    printf("%d\n",sizeof(a)); //4:int型占4字節

    int p; //聲明指針,沒有指向,就是野指針。野指針使導致程序崩潰的主要原因
    p = 1; // 為地址上賦值1,會出現錯誤 [1] 6285 segmentation fault (core dumped) a.out

    int *p = NULL; // 空指針使合法的,野指針是非法的

}


/** int p 與p一致
/

include <stdio.h>

void main(){
int a =100;
int p = &a;
int
q;
q = &a;
printf("p:%u,q:%u\n",p,q); // p:2373644580,q:2373644580
printf("p:%d,q:%d",p,q);// p:100,q:100
}

  1. 指針的兼容性

    include <stdio.h>

    void main(){
    int a = 0X1013;
    char b = 13;

    int p;
    p=&a;
    printf("%x",
    p); // 1013

    p=&b; // 10130d 用int指針指向char類型變量,因為int指針指向了連續4字節的地址,所以除了1013外后面還有3個字節的數據當做一體來指向
    printf("%x",*p);

    int q;
    char buf[] = {0x12,0x34,0x56,0x78,0x90,6,7,8,9,10};
    q = buf;
    printf("%x\n",
    q);

    //指針不兼容的情況
    float i = 3.14;
    int p = &i;
    printf("%d",
    p); // 此時并不能打印出3,這就是指針類型不兼容
    }

  2. 指向常量的指針和指針常量
    void main(){
    int a = 100;
    const int p = &a; // 指向常量的指針只能讀內存,但可以改變指向
    printf("%d\n",
    p); // 100

    int const q = &a; // 常量指針可以讀寫指向的內存,但不能改變指向
    q = 200;
    printf("%d\n",*q); //200
    printf("%d\n",a); //200
    }

  3. 數組與指針
    void printarr(char s[]){
    int i;
    for(i=0;i<10;i++){
    printf("s[%d]\n", %d);
    }
    }
    void main(){
    char buf[10] = {1,2,3,4,5,6,7,8,9,0};
    char p = buf;
    char
    p1 = &buf[0];
    char *p2 = &buf[1];

    printf("%d\n", p); //1
    printf("%d\n",
    p1); //1
    printf("%d\n", *p2); //2

    p2 ++; // 指針是地址號,p+1
    *p2 = 100; // 更改為100
    printarr(buf);
    }
    -------------------------------------
    ip地址在網絡中不是以字符串傳輸,字符串太長,而是以DWORD傳輸(4字節),飆戲那位一個int數值

    include<stdio.h>

    void change2char(int i){
    unsigned char p = &i;
    printf("%d.%d.%d.%d",
    p,(p+1),(p+2),(p+3));
    }
    void main(){
    int a = 0;
    unsigned char
    p1 = &a; // 無符號使得最大值為255
    *p1 = 192;
    /**(p+1) = 168;
    (p+2) = 0;
    (p+3) = 1;/
    p1++;
    p1 = 168;
    p1++;
    p1=0;
    p1++;
    p1=1;
    printf("ip:%d",ip);
    // a已經被改變為一個ip地址
    change2char(a);
    }
    ------------------
    int s2ip(char s[]){
    int a = 0;
    int b = 0;
    int c = 0;
    int d = 0;
    sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d);
    printf("a=%d,b=%d,c=%d,d=%d\n", a,b,c,d);
    int ip=0;
    char p = &ip;
    p++;
    p = a;
    p++;
    p=b;
    p++;
    p=c;
    p++;
    *p=d;
    }
    void main(){
    char s[] = "192.168.0.1";
    printf("%d\n", s);
    }

eg:用指針合并數組
void main(){
char s1[] = "hello";
char s2[]="world";
char p1 = &s1;
char
p2 = s2;
while(p1){ //p1指向的元素為0時跳出循環
p1 ++;
}
while(p2){
p1++ = *p2++; // 先指向,后指針移位
}

}

指針運算不是簡單地數值加減法,而是指針指向的數據類型在內存中所占字節數為倍數的運算
void main(){
int buf[10];
int *p = buf;
printf("%d\n", p); //-1670667696
p+=3;// 實際上內存地址好漲了12
printf("%d\n", p);
p = (int)p+3; // 此時把內存地址號強制轉為int型,此后加3就是數值運算:-1670667684
printf("%d\n", p); // -1670667681

char *p2 = buf;
p2 += 4;
printf("%d\n", *p2);  // 0short *pp1 = &buf[1];
short *pp2 = &buf[3];
printf("%d\n", pp2 - pp1);   // buf3和buf1差8個字節,但是一個short指針變量指向2個字節的連續塊,所以值位4

}


二維數組的指針
void main(){
int buf[2][3] arr = {{1,2,3},{4,5,6}};
// int p[3] ; //定義一個指針數組
int (
p)[3]; // 定義了一個指針,指向int[3]這種數據類型,也叫作指向二維數組的指針
printf("%d\n",sizeof(p)) ; //8 因為指針在64位系統都是占8位
printf("%d,%d\n",p,p+1); //相差12字節

p = buf; //p指向了二維數組的第一行
p++; //p指向了二維數組的第二行// 指針遍歷
int i,j;
for(i=0;i<2;i++){for(j=0;j<3;j++){//printf("%d\n", *(*(p+i)));   //打印結果1,1,1,4,4,4printf("%d\n", *(*(p+i)+j);  // 遍歷每個元素printf("%d\n", p[i][j]);  // 每個元素,與上面相等}
}

}

指針作為函數參數
void test(int n){
n++;
}
void test2(int p){
(
p)++;
}
void main(){
int i =100;
test(i); //c語言是值傳遞,所以普通函數參數的改變是不能傳回給參數的,但如果參數是地址,改變地址上的值
printf("%d\n", i);

test2(&i);
printf("%d\n", i);

}

一維數組作為參數的指針
void swap(int a,int b){
int tmp = a;
a = b;
b = tmp;
}
// 當數組名作為參數傳遞時,數組名解析為數組首地址指針,所以多寫為void setarray(int buf)
void setarray(int buf[]){
printf("占%d位\n", sizeof(buf)); //8:64位系統指針占8字節
buf[0] = 100;
buf[1]=200;
int i;
for(i=0;i<10;i++){
printf("%d\n", buf[i]);
}
}
void setarray(int
buf,int n){ // n為數組的長度
buf[0] = 100;
buf[1]=200;
int i;
for(i=0;i<n;i++){
printf("%d\n", buf[i]);
}
}
void main(){
int a = 10;
int b = 20;
swap(&a,&b);
printf("%d,%d\n", a,b);

// 因為數組名是數組的首地址,所以數組名作為行參傳遞的時候,就相當于傳遞的指針
int buf[]= {1,2,3,4,5,6,7,8,9,0};
printf("%d\n", sizeof(buf)); //40
setarray(buf,sizeof(buf)/sizeof(int));  // 數組長度寫成sizeof(buf)/sizeof(type)

}

二維數組的指針作為函數參數
void printarr2(const int (*p)[4],int m,int n){//mn分別是二維數組的第一個和第二個角標。通過const標識,保護函數不會改變數組中的值
int i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("p[%d][%d] = %d\n", i,j,p[i][j]);
}
}
}
int main{
int buf[][4] = {{1,2,3,4},{5,6,7,8}};
printarr2(buf,sizeof(buf)/sizeof(buf[0]),sizeof(buf[0])/sizeof(int));
return 1;
} 33 min 18 s
--------------計算二維數組每一列的平均值----------------

include<stdio.h>

void main(){
int buf[2][4] = {{1,2,3,4},{5,6,7,8}};
for(int i=0;i<4;i++){
int sum = 0;
for (int j = 0; j < 2;j++)
{
sum += buf[j][i];
}
printf("%d\n", sum);
}
}

函數指針
函數也有地址,這個地址存放的是代碼
int getmax(int x, int y){ // 也可以寫為int getmax(int x, int y) 意思一樣,函數名同數組名一樣,都是即表示本身,又表示指向自身開頭的指針
return x>y?x:y;
}
int getmin(int x, int y){
return x<y?x:y;
}
int main(){
int (
p)(int,int) ; //定義一個指針p指向函數,該函數反獲知是int,2個輸入參數是(int,int)
int fun = 0;
scanf("%d",&fun);
if(fun==1){
p = getmax; //函數名就是指針
}else{
p = getmin; //函數名就是指針
}
int i = p(2,4); // 通過函數指針調用函數
printf("%d\n", i);
}

31min
函數指針是指向函數的指針變量
void p(int,char) // 定義一個函數,函數名為p,參數為int和char類型,返回值為void類型
void (
p)(int,char) // 定義一個指針,該指針指向函數。返回值為void, 參數為int和char類型
-----------------------------------------------
將一塊內存初始化為0最常見的方法:memset(buf,值,size(buf)):要設置的內存地址,設置的值,內存大小(單位字節)
memcpy(buf2,buf1,sizeof(buf1)):將buf1的內容全部拷貝到buf2
memmove (buf2,buf1,sizeof(buf1));

include <string.h>

include <stdio.h>

void main(){
int buf[10] = {1};
memset (buf,0,sizeof(buf));

int buf2[] = {1,2,3,4,5,6,7,8,9,0};
int buf3[];
memcpy(buf3,buf2,sizeof(buf2));
for (int i = 0; i < 10; i++)
{printf("%d\n", buf2[i]);
}

}

指針數組與多級指針
int main(){
int a[10]; //一個數組,數組內有10個元素,64為系統中,指針站8字節,64位
printf("%d,%d\n", sizeof(a),sizeof(a[0])); // 40,8
short
b[10];
printf("%d,%d\n", sizeof(b),sizeof(b[0])); // 40,8
}

int main(){
int a = 10;
int p = &a;
int pp = &p; // pp指向p的地址
pp= 100; //修改a的值 如果
p=100,則是把p這個地址號改為100
printf("a=%d\n",a);
int ppp = &pp;
a = ppp;
println("%d,%d",
ppp,&pp); // 三級指針的內容=二級指針的地址
}

指針完成字符串操作
int mian(){
char buf[100] = "hello world";
char p = buf;
p += 5;
p = 'c';
printf("%s\n", buf); // hellocworld
return 1;
}

如果字符串中包含0字符,則打印到此位置就結束了。淚庫里面的函數都是有\0識別字符串結尾的


c語言主函數也是有輸入參數的,而且有2個,第一個表示輸入參數的個數,第二個是一個指針數組,每個指針指向char類型
輸入參數個數:永遠大于1,因為程序名本身作為一個輸入參數
指針數組:這個指針數組的長度是第一個參數,每個元素指向一個輸入參數
int main(int argc,char *args[]){
if(argc!=4){
printf("請輸入完整的數學表達式,以空格分開" );
return 0;
}

int a = atoi(args[1]);
int b = atoi(args[3]);switch(args[2][0]){case '+':printf("%d\n", a+b);break;case '-':printf("%d\n", a-b);break;case '*':printf("%d\n", a*b);break;case '/':if(b){printf("%d\n", a/b);}break;
}
return 0;

}

內存管理

  1. 作用域
    (1)文件作用域:全局變量,定義在函數外面的變量
    int a=10;
    void main(){
    int a = 20; // 函數內部可以再次定義和全局變量同名的變量,定以后a為20,沒有定義為10
    printf("%d\n", a);
    }
    (2)extern關鍵字

    include "c.h" #在這個文件中定義了age=10

    extern int age ;// 該變量在其他文件中定義了,連接時使用就可以了。這個另一個文件需要時.c文件,不能是.h文件。gcc編譯時后面加上兩個文件名
    setage(int n){
    age = n;
    }
    getage(){
    printf("%d\n", age);
    }
    void main(){
    setage(11);
    getage(); // 11
    }
    (3)auto自動變量:不寫auto,就默認為auto變量
    (4)register 變量:建議變量放到空閑的寄存器中
    寄存器變量不能取地址操作
    (5)static 變量:只初始化一次,程序運行期間一直存在 。static只是增大了變量的存在時間,卻沒增大變量的作用域
    一旦全局變量被static修飾,則這個變量的作用域被限制在該文件內部,其他文件不能用extern使用
    void mystatic(){
    static int a = 0; // 不加static,循環打印每次打印都是0,加上static每次打印+1
    printf("%d\n", a);
    a++;
    }
    void main(){
    for (int i = 0; i < 10; ++i)
    {
    mystatic();
    }
    }

  2. c語言中,函數都是全局的。
    (1)如果函數前面加了static,則函數被限制在文件內部使用
    (2)函數前面加不加extern一樣。而變量前面的extern是可以省略的,比如:int a; 如果其他文件中定義了int a=10,則此處的變量是聲明,如果其他文件中沒有定義a,則此處是定義
    (此處也是說明c語言并不嚴謹)

  3. c語言內存管理
    程序在內存中分為4個區域:
    (1)堆
    (2)棧:此部分的內存表現為先進后出。所有自動變量,函數行參都有編譯器自動放到棧中。當一個自動變量超出其作用域時,自動從棧中彈出。先進入棧中的變量放到大內存號中
    (3)靜態區:所有的全局變量,以及程序中的靜態變量都存儲在靜態區
    (4)代碼區:程序在被操作系統加載到內存中時,所有可執行代碼加載到代碼段,這塊內存不能再程序運行期間修改
    int c= 0;
    void main(){
    int a=1;
    int b=2;
    static int d = 3;
    printf("%d,%d,%d,%d,%d\n", &a,&b,&c,&d,main); // 1321929276,1321929272,6295612,6295604,4195632
    }
    上述程序的a和b在棧區,所以地址號緊挨著:1321929276,1321929272 發現a所在的內存號在高位。
    c和d在靜態區:地址號緊挨著
    main在代碼段

二進制文件讀寫

  1. fscanf:讀文件時,可以根據固定格式讀取
    fprintf:寫文件,和printf一樣,可以定義輸出格式,只是輸出到文件中

include <stdio.h>

include <string.h>

include <stdlib.h>

void main(){
FILE *p = fopen("a.txt","r");
while(!feof(p)){
int a,b;
fscanf(p,"%d + %d =",&a,&b); //文本格式為a + b =,從中截取a,b
printf("%d,%d",a,b);
}
}

void main(){
FILE *p = fopen("a.txt","w");
char buf[100] = "hello world fuck ";
int a=1,b=2;
fprintf(p,"%s,%d,%d",buf,a,b);
fclose(p);
}

  1. fread與fwrite讀寫二進制文件
    上面的函數只能操作字符文件,字符文件每次只能讀一行
    void main(){
    FILE *p = fopen("a.txt","rb"); // 以二進制方式讀取
    char buf[100] = {0};
    fread(buf,sizeof(char),1,p); // 讀取字節的緩沖區,讀取單位,一次讀取幾個單位,文件指針
    printf("%s\n", buf);
    fclose(p);
    }

指針數組與多級指針
int main(){
int a[10]; //一個數組,數組內有10個元素,64為系統中,指針站8字節,64位
printf("%d,%d\n", sizeof(a),sizeof(a[0])); // 40,8
short
b[10];
printf("%d,%d\n", sizeof(b),sizeof(b[0])); // 40,8
}

int main(){
int a = 10;
int p = &a;
int pp = &p; // pp指向p的地址
pp= 100; //修改a的值 如果
p=100,則是把p這個地址號改為100. pp就是地址號的內容相當于改變p的內容,pp就是地址號中記錄的地址號的內容
printf("a=%d\n",a);
int
ppp = &pp;
a = ppp;
println("%d,%d",
**ppp,&pp); // 三級指針的內容=二級指針的地址
}

指針完成字符串操作
int mian(){
char buf[100] = "hello world";
char p = buf;
p += 5;
p = 'c';
printf("%s\n", buf); // hellocworld
return 1;
}

如果字符串中包含0字符,則打印到此位置就結束了。淚庫里面的函數都是有\0識別字符串結尾的


c語言主函數也是有輸入參數的,而且有2個,第一個表示輸入參數的個數,第二個是一個指針數組,每個指針指向char類型
輸入參數個數:永遠大于1,因為程序名本身作為一個輸入參數
指針數組:這個指針數組的長度是第一個參數,每個元素指向一個輸入參數
int main(int argc,char *args[]){
if(argc!=4){
printf("請輸入完整的數學表達式,以空格分開" );
return 0;
}

int a = atoi(args[1]);
int b = atoi(args[3]);switch(args[2][0]){case '+':printf("%d\n", a+b);case '-':printf("%d\n", a-b);case '*':printf("%d\n", a*b);case '/':if(b){printf("%d\n", a/b);}
}
return 0;

}

棧:不會很大,棧空間是操作系統為每個程序固定分配的大小以k為單位。棧空間是操作系統為每個程序固定分配的大
每個線程都有自己的棧
int main(){
char array[102410241024] = {0}; //定義一個超大的數組就會棧溢出
return 0;
}

堆:堆沒有大小限制,只是無力限制。但是堆內存不能由編譯器自動釋放

include <stdlib.h>

int main(){
int p = malloc(sizeof(int)10); // 在堆中申請了10個int的大小
char p2 = malloc(sizeof(char)10);

memset(p,0,sizeof(int)*10);  // malloc申請的堆內存 ,在代碼的操作上相當于數組
for(int i=0;i<10;i++){ p[i] = i;  // 操縱數組
}free(p) ; // 釋放通過malloc申請的堆內存
free(p2);

}

  1. 不能將一個棧變量的地址作為函數的返回值,e因為函數結束后,棧變量被釋放
    eg: int *geta(){
    int a = 10;
    return &a;
    }

  2. 但是可以把堆變量的地址作為函數返回值返回
    int geta(){
    int
    p = malloc(sizeof(int));
    return p;
    }

int main(){
int *p = geta();
malloc(p);
}

  1. 一個經典的錯誤模型:
    void getheap(int p){
    p = malloc(sizeof(int)
    10);
    }
    void main(){
    int *p = NULL;
    getheap(p); // 此處是錯誤的,因為函數的參數是值傳遞,雖然語義是getheap(p),但是mallo的內存地址只是付給了函數的形參,函數退出后,形參的值并不能傳遞給實參(指針的值也一樣不能傳遞),導致p還是空指針,下面的p[0]=1發生空指針異常
    p[0] = 1;
    printf("%d\n", p[0]);
    free(p);
    }

  2. 經典的正確模型:二級指針作為形參,對上面錯誤模型的改寫
    void getheap(int **p){
    p = malloc(sizeof(int)10); // 所致地址號的內容,就是指以及指針的內容
    }
    void main(){
    int *p = NULL;
    getheap(&p); // 此處是錯誤的,因為函數的參數是值傳遞,雖然語義是getheap(p),但是mallo的內存地址只是付給了函數的形參,函數退出后,形參的值并不能傳遞給實參(指針的值也一樣不能傳遞),導致p還是空指針,下面的p[0]=1發生空指針異常
    p[0] = 1;
    printf("%d\n", p[0]);
    free(p);
    }


不能用變量聲明一個數組,eg:int arr[i], 如果申請內存的大小靠程序運行期間決定,那么就要使用 malloc(sizeof(int)*i),要記得free
代碼區和靜態區的大小都是固定的,靜態區是編譯器根據代碼提前申請的。堆和棧所占大小是動態,但是棧有一個操作系統規定的棧上線

cd /proc // 每個進程號都是一個文件
cd pid
cat maps // 查看內存映射,這里列出了代碼段,堆,棧,靜態區的使用情況(棧的預設范圍)


堆的分配和
操作系統分配內存的單位不是字節,而是頁。cat smps之后,發現堆有個項是內存頁大小:4k。頁太大,浪費的內存空間太大,但操作系統維護起來很簡單,因為不用頻繁的申請釋放內存

malloc申請的堆內存上會存在舊數據,所以申請后需要用memset清空 : memset(p,p,sizeof(int)10)
這兩句話的簡寫就是calloc,這樣內存上全部是0
int main(){
char
p = calloc(10,sizeof(char)); // 申請大小是10*sizeof(char)
}
relloc用于擴展已經申請的堆內存,在原有內存上,增加連續的內存,如果沒有連續空間可擴展,則會新分配一個連續空間,將原有內存拷貝到新空間,然后釋放原有內存
但是relloc只申請,不打掃(內存中有舊數據)

rello(p,新的內存大小),.如果p為NULL,則realloc等于malloc,不指定分配空間的起始位置
void main(){
char p1 = calloc(10,sizeof(char));
char
p2 = realloc(p1,5); // p1指向的地址變為5
// realloc(NULL,5) = malloc(5)
}


結構體:

include <string.h>

struct student{
char name[100];
int age;
int sex;
}; // 聲明一個結構體

void main(){
struct student st; // 定義了一個結構體的變量,名字叫st
//struct student st2 = {.name="周杰倫",.sex=0}; 結構體中通過.來給屬性賦值
struct student st3 = {0}; // 定義結構體變量,并把結構體變量的所有屬性設置為0
struct student st4;
memset(&st,0,sizeof(st)); // 結構體變量初始化
st.age = 25;
st.sex = 1;
strcpy(st.name,"劉德華");

printf("%d\n", st.age );

}

  1. 結構體的內存對齊模式:
    結構體在內存中是一個矩形,而不是一個不規則形狀,以結構體中占地最大的元素對齊。而且不同類型的屬性要跨偶數個字節
    struct A
    {
    int a; // 4字節
    char b; // 以4字節對齊
    }; // 總共8字節

struct B
{
char a; // 1個字節
short b; // short不同于char類型,所以要夸2字節,從第3個字節開始對齊 , 占2個字節
char c; // 占1個字節
int d; // 第二個8字節對齊行
long long e; // 8個字節,占地最多的屬性,所以其他屬性以8字節對齊;
};

printf("%d\n", sizeof(B));

  1. 結構體成員的位字段
    struct A
    {
    char a:2; // char占1個字節,但是這樣聲明的結構體中的a字段,只有前2bit能通過結構體使用,但值得注意的是此時整個結構體仍然占1個字節而不是2bit
    };
    void main(){
    struct A a;
    a.a = 3; //二進制11,有符號char,所以會打印-1
    printf("%d\n", );
    }

struct B
{
char a:2;
unsigned char b:4;
char c:1;
};

printf("%d\n", sizeof(struct B));

  1. 指針操作結構體不了的內存地址
    struct H
    {
    char a; // 1個字節,但是后三個字節由于結構體對齊而操作不了
    int b;
    };

void mian(){
struct H h = {1,2};
char p1 = &h;
p1 ++;
p1 = 4;
(++p1) = 5;
(++p1) = 6;
*(++p1) = 7;
printf("%d\n", &h+4);
}

  1. 結構體模仿數組(用字段地址連續的結構體成員)
    struct D
    {
    char a;
    char b;
    char c;
    };

void main(){
struct G g;
char *p = &g;
p[0] = 'a';
p[1] = 'b';
p[2] = 'c';
printf("%d,%d,%d\n", g.a,g.b,g.c);
}

  1. 結構體數組
    struct People
    {
    int age;
    char name[10];
    };

void main(){
struct People p[3] = {{11,"aa"},{22,"bb"},{33,"cc"}};
for (int i = 0; i < 3; ++i)
{
printf("%d,%s\n", p[i].age,p[i].name);
}
}

  1. 結構體可以變相實現數組的賦值
    struct A
    {
    char arr[10];
    };

void main(){
struct A a1 = {"hello"};
struct A a2= {0};
a2 = a1;
printf("%s\n", a2.arr);
}

  1. 結構體的嵌套
    struct A{
    char a;
    char b;
    }
    struct{
    struct A a;
    int b;
    } // 一共占8字節

結構體賦值,其實就是結構體之間內存的拷貝
struct strss
{
char s[100];
};
void main(){
struct strss s1,s2;
strcpy(s1.s,"hello");
// s2 =s1; 這句話等同于下面的memcpy,結構體是變量,所以可以賦值
memcpy(&s2,&s1,sizeof(s1));
printf("%s\n", s2.s);
}

指向結構體的指針操作結構體
void main(){
struct A a;
struct A p = &a;
// (
p).a = 10; 這樣寫是對的,但是太麻煩,沒人這么寫
p -> a = 10; // 用->代表結構體的一個成員
}

指向結構體數組的指針
<string.h><stdlib.h>
void main(){
struct A *p ;

/* struct A array[10] = {0};
p =  array;  // 指針指向數組首地址 *///也可以在堆中創建結構體
p = malloc(sizeof(struct A) * 10);
memset(p,0,sizeof(struct A) * 10)
struct A *array = p;p->a = 10;
p->b = 11;p++;  // p已經變化了,所以free時要從頭free,就是array。如果free(p),就從當前p的位置往下數10個字節,這樣就free了不屬于結構體數組的內存,運行報錯
p->a = 3
p->b = 4;for (int i = 0; i < 10; ++i)
{printf("%d,%d\n", array[i].a,array[i].b);
}free(array);

}

結構體作為函數參數時,盡量使用指針傳遞參數,不要直接使用結構體作為形參
因為結構體和int等變量一樣,作為參數傳遞時是值拷貝傳遞,如果傳遞指針,則函數只需要拷貝8字節的地址號,而且如果函數想要操作結構體的話,也只能通過指針進行參數操作

void setname(struct student a,int age){
a->age = age;
}

四。聯合體

  1. 聯合體所占內存的大小等于聯合體中最長屬性所占狄村的大小,聯合體的所有屬性公用一塊內存地址
    union A{
    int a;
    char b;
    }
    void main(){
    union A a;
    a.a = 0X12345678;
    printf("%d\n", sizeof(union A)); // 4 ,最長的int屬性,棧4字節
    printf("%p,%p\n", &a.a,&a.b); // 2個地址一樣
    printf("%d,%d\n", a.a,a.b); // 12345678, 78
    }

一個經典的錯誤寫法
union A{
int a;
char *b;
}
void main(){
union A a;
a.b = malloc(100);
a.a = 100;
// 此時改變了a的值就是改變了b的值,下面free的地址就不再是原來的地址,會報錯
free(b)
}
所以:如果一個聯合體中包含了含有指針變量,那么一定要在使用完這個指針后,病free掉它,才能使用聯合體的其它成員

五。 枚舉

  1. 枚舉是一個int常亮,不能改變他的值
  2. 美劇中的每個量都相當于#define x 整數
    //#define red 0 相當于下面的枚舉定義
    //#define black 1
    //#define yellow 2
    enum A{
    red,black,yellow
    };
    void main(){
    printf("%f\n", red); // 打印0
    }

六。typedef

  1. typedef char BYTE; 自定義一個BYTE類型,作為char類型
    相當于

    define BYTE char

  2. typedef 通常用于簡化結構體變量的名字為一個簡短的名字
    struct A{
    int a;
    }
    typedef struct abc A; // 本來聲明變量時需要struct A a, 現在只需要abc A即可

  3. typedef定義結構體的簡寫形式
    typedef struct A{
    int a;
    }abc; // 張揚相當于上面2句話的簡寫, A也可以不寫形成第三種寫法,如下

typedef struct {
int a;
}abc;

  1. typedef定義函數指針

    include <string.h>

    include <stdio.h>

    void mystrcat (char s1,char s2){
    strcat(s1,s2);
    }
    // 定義一個函數,有三個參數,函數指針,形參1,形參2
    void
    test(void (p)(char ,char ),char s1,char s2){//函數指針p,返回值是void ,形參有兩個,都是char *類型
    p(s1,s2);
    }

// 上面的test函數太復雜,因為參數里包含一個函數指針
typedef void (MYSTRCAT)(char ,char ) // 把一個函數指針定義為MYSTRCAT類型

void main(){
MYSTRCAT aa[10];
char s1[100] = "hello";
char s2[5] = "world";
test(mystrcat,s1,s2);
printf("%s\n", s1);
}

七。文件操作
寫文件

include <stdio.h>

int main(){
FILE *p = fopen("aaa.txt","w");
fputs("hello world\n",p);
fclose(p);
return 0;
}

讀文件

include <stdio.h>

include <stdlib.h>

int main(){
char s[1024] = {0};
FILE *p = fopen("aaa.txt","r");
while(!feof(p)){ // 如果文件已經到最后,則文件返回真)
memset(s,0,sizeof(s));
fgets(s,sizeof(s),p); // 從p位置讀取10個字節
printf("%s", s);
};

    fclose(p);return 0;

}

如果文件不存在,則新建文件,如果存在就追加文件內容
fopen("aaa.txt","a");

轉載于:https://www.cnblogs.com/72808ljup/p/5785541.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372483.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372483.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372483.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Centos】yum 安裝mariaDB

[dream361za ~]$ sudo yum search mariadb #查找需安裝的包 mariadb-libs.x86_64 : The shared libraries required for MariaDB/MySQL clients #mariadb客戶端 mariadb-server.x86_64 : The MariaDB server and related files #mariadb服務 [dream361za ~]$ sudo yum instal…

對口升學計算機組裝與維護,對口升學信息技術(計算機)類2017年專業課考試大綱...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓山西省中等職業學校對口升學考試大綱信息技術類專業本考試大綱是以國家中等職業學校計算機專業教學指導方案為依據&#xff0c;以中等職業教育國家規劃教材《物理(電工電子類)》(李廣華、郝翠蘭主編&#xff0c;電子工業出版社)、《…

網頁性能優化(初窺)

面試的時候經常會被問到的有關于前端性能優化這一塊的問題&#xff0c;扯扯個人的理解 第一條&#xff1a;減少 HTTP 次數的請求 80%的最終用戶響應時間花在前端程序上&#xff0c;而其大部分時間則花在各種頁面元素&#xff0c; 如圖像、 樣式表、 腳本和 Flash 等&#xff0c…

STM32F10x_硬件I2C主從通信(輪詢發送,中斷接收)

Ⅰ、寫在前面 關注我分享文章的朋友應該知道我在前面講述過&#xff08;軟件、硬件&#xff09;I2C主機控制從機EEPROM的例子。在I2C通信主機控制程序是比較常見的一種&#xff0c;可以說在實際項目中&#xff0c;很多應用都會使用到I2C通信。但在實際項目中作為I2C從機的應用相…

JavaFX 2:創建登錄表單

在本教程中&#xff0c;我將使用JavaFX 2和CSS設計漂亮的Login Form 。 它是經典的登錄表單&#xff0c;帶有用戶名和密碼以及登錄按鈕。 為了遵循本教程&#xff0c;我強烈建議您查看以下這些教程&#xff1a; Eclipse IDE中的JavaFX 2入門 JavaFX 2&#xff1a;HBox JavaFX…

c html導出成word,html轉word-html如何轉換成WORD

1、打開HTML文件&#xff0c;點擊菜單欄文件→使用MicrosoftOfficeWord編輯&#xff0c;之后系統會自動打開Word并顯示HTML文件的內容&#xff0c;這是保存即可。2、如果找不到“使用MicrosoftOfficeWord編輯”的話&#xff0c;點擊菜單欄工具→Internet選項→程序→HTML編輯器…

怎么解決tomcat占用8080端口問題

怎么解決tomcat占用8080端口問題 相信很多朋友都遇到過這樣的問題吧&#xff0c;tomcat死機了&#xff0c;重啟eclipse之后&#xff0c;發現Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.The server may already be running in…

ADO Recordset 對象鏈接

http://baike.baidu.com/link?url4Xdc46R8M5uj-BbOGaH761N5oDEYlGQJFeR2WbPwx1iQBusAUKU3qbWcHZCMmayatj9nzxPW7HdPToL6roD3Y_ 轉載于:https://www.cnblogs.com/loanhicks/p/5788451.html

mvc4 html.beginform,MVC4 Html.BeginForm在Internet Explorer中提交按鈕 9不工

我已經寫在ASP.NET MVC4 /剃刀的形式。 該表格后很完善在Firefox和Chrome&#xff0c;但由于某種原因在Internet Explorer 10和11&#xff0c;“提交”按鈕沒有反應。 (Internet Explorer 9的作品也不錯)。這是我的看法形式的樣子&#xff1a;using (Html.BeginForm("MyAc…

頁面傳值的方法 和JSON與字符串和對象之間的轉換

json數據解析 就是將json轉換為數組或對象 json數據序列化 就是將數組或對象轉化為json轉載于:https://www.cnblogs.com/yaomengli/p/6678709.html

JasperReports JSF插件用例系列

這是文章系列的切入點&#xff0c;在該系列文章中&#xff0c;我將嘗試介紹JasperReport JSF Plugin的一些用例&#xff0c; JasperReport JSF Plugin是一種工具&#xff0c;旨在輕松地將為JasperReports設計的業務報告集成到JSF應用程序中。 該系列中描述的所有示例都可以從Ja…

回歸分析

一元線性回歸模型&#xff1a; 一元線性回歸分析的主要任務是&#xff1a; 解得&#xff1a; 三、檢驗、預測與控制&#xff1a; 1&#xff09;F檢驗法&#xff1a; 2&#xff09;t檢驗法 3&#xff09;r檢驗法 3預測&#xff1a; 四、可線性化的一元非線性回歸&#xff08;曲線…

與傳統的計算機硬件系統相比,計算機一級名詞解釋

目前微型機中普遍采用的字符編碼是ASCII碼。它是用7位二進制數對127個字符進行編碼&#xff0c;其中前32個是一些不可打印的控制符號。多媒體系統由主機硬件系統、多媒體數字化外部設備和多媒體軟件三部分組成。機器語言和匯編語言都是"低級"的語言&#xff0c;而高級…

ubuntu安裝jdk1.8

sudo add-apt-repository ppa:webupd8team/javasudo apt-get updatesudo apt-get install oracle-java8-installer 轉載于:https://www.cnblogs.com/czwangzheng/p/5793488.html

【java】對象變成垃圾被垃圾回收器gc收回前執行的操作:Object類的protected void finalize() throws Throwable...

1 package 對象被回收前執行的操作;2 class A{3 Override4 protected void finalize() throws Throwable {5 System.out.println("在對象變成垃圾被gc收回前執行的操作。");6 }7 }8 public class Test_finalize {9 public static void main(…

服務器 風扇測試軟件,圖解服務器風扇安裝的正確方法

一般不是太垃圾的機箱總有兩個地方可以裝風扇&#xff0c;前面的一般在硬盤托架處&#xff0c;后面的一般在電源下面&#xff0c;鍵盤口上方。有的機箱出廠就已經裝好1&#xff5e;2個風扇了。圖中越紅的區域溫度相對越高。應該什么樣的風道合理呢?1、前后都裝機箱風扇的情況應…

處理Weblogic卡住的線程

定義或卡線是什么&#xff1f; 如果線程 在設定 的時間 內連續工作&#xff08;非空閑&#xff09;&#xff0c;則WebLogic Server會將其診斷為阻塞 。 您可以通過更改在診斷出線程被阻塞之前的時間長度&#xff08; Stuck Thread Max Time &#xff09;&#xff0c;以及通過更…

控件自定義左鍵點擊消息相應函數的問題(請懂的人來解答一下)

【問題描述】 自定義CTouchInputEdit類&#xff0c;繼承自CEdit。 實現一個新的功能&#xff0c;當點擊edit控件的時候&#xff0c;彈出一個輸入框。 下面的代碼片段1&#xff0c;當點擊控件之后&#xff0c;會彈出輸入框&#xff0c;退出輸入框以后&#xff0c;在窗體的任何位…

電話圈(floyd)

題意&#xff1a; 如果兩個人相互打電話&#xff0c;則說他們在同一個電話圈里。例如&#xff0c;a打給b&#xff0c;b打給c&#xff0c;c打給d&#xff0c;d打給a&#xff0c;則這4個人在同一個圈里&#xff1b;如果e打給f但f不打給e&#xff0c;則不能推出e和f在同一個電話圈…

計算機二級網址打不開,大神為你解決win7系統打不開二級網頁鏈接的操作教程...

許多win7系統電腦的時候,常常會遇到win7系統打不開二級網頁鏈接的情況&#xff0c;比如近日有用戶到本站反映說win7系統打不開二級網頁鏈接的問題&#xff0c;但是卻不知道要怎么解決win7系統打不開二級網頁鏈接&#xff0c;我們依照首先我們打開IE瀏覽器&#xff0c;然后點擊上…