C語言 深入學習


浮點數:

x = Mx*2^Ex為一個規格化浮點數,Mx為x的尾數,Ex為x的階碼。
1e-6:表示1 * 10 ^ (-6)。
編譯時執行:
sizeof是運算符(而非函數),在編譯時執行,不會導致額外的運行時間開銷。
三元運算符:是右結合運算。

數據類型:

char:占1個字節
short:占2個字節
int:占4個字節
float:占4個字節
double:占8個字節

一個變量本身定義為常量const時,必須初始化
常量指針:不能通過常量指針修改其指向的值(只是從p的角度來看)。
const int *p;
int const *p;
指針常量:指針其本身就是常量(故定義時須初始化),而且也不能通過這個指針修改其指向:
int * const p = &a;

數組指針(指向一維數組的指針 / 行指針):char (*p)[10] —— 打括號就是說,先看括號里面的。故可以這樣理解:
舉例, 二維數組char a[10][10], char (*p)[10];p = a。
首先確定,char (*p)[10]只是將p定義為一個指針(其作用是指向某個數組整體,而不是這個數組中的某個元素);10就是指定了指向的數組的長度
p = a就讓p指向了a[0]這個數組;於是p++,就讓p指向了下一個數組a[1];
所以若p = a,那么*p就是獲得a[0]這個數組。
既然char (*p)[10]是指向數組的指針,那么通用的使用就是:把數組的地址給p,即char (*p)[10];char s[]="abcd";p=&s;
而二維數組a[][]的數組名a的含義是:a == &a[0];

指針數組(數組元素是數組):char *p[10] —— 沒有括號,所以可以這樣看:這是一個數組,其元素類型是char *p。這里是一個放了10個指針的數組。所以p[0]=arrary;是指向數組首元素,而不是整個數組

標准輸入輸出格式:

當數據的實際位寬大於printf中的指定位寬時,將按照數據的實際位寬輸出數據

//輸出
printf("%ld\n", test);     /* 以長整型格式輸出 */
printf("%f\n", test);       //輸出單雙精度浮點數
printf("%4.2f\n", test);       //輸出數據共占4位(包括小數點),有2位小數;其他數據類型也是如此(自然有些是沒有小數的)
printf("%u", n);    //n是unsigned int
printf("%s\n", str);    //輸出一個字符串
printf("%p\n, p);   //輸出地址

//輸入
/*scanf:返回值為成功接收到的、輸入的變量值個數。
如scanf("%d%d%d",i,j,k,a,b)則只能返回3(因為只能接收三個)*/
char *str = (char *)malloc(N * sizeof(char)); scanf("%s", str);        //輸入一個字符串
scanf("%lf", &test);       //輸入雙精度浮點數
scanf("%d,%d", &a, &b);        //輸入必須與指定的格式相同。比如這里有",",那么在Console中輸入也需要","

隨機數:

//#include <stdlib.h>:rand、srand
//#include <time.h>:time
srand(0);
printf("%d\n", rand());        //rand對應於srand,只要srand的種子不變,rand的隨機值也不會變;rand的范圍是0 - int
srand(time(NULL));    //srand(unsigned)
printf("%d\n", rand());        //time函數返回當前時間(秒);參數param如果不為BULL,則time會將時間賦給param。故這樣一來隨機數就會一直改變(真正實現隨機)
printf("%d\n", rand() % 100 + 1);        //生成1-100的隨機數:rand() % 100是0-99

指數函數:

pow(x, y)        //表示x的y次方 —— math.h

數組:

初始化:

int count[10] = {0};    //長度為10的數組,全都初始化為0;定義數組時,數組長度只能是常量,不能是變量或含變量的表達式

二維數組:

多維數組在內存中實際上都是一維存儲的。例如二維數組,我們只是在邏輯上可以理解為二維的矩陣,但實際上二維數組是一維數組,每個元素還是一維數組,所以整個二維數組都是"一條線"
1、因此int a[3][3]={1...9};int (*p)[3] = a;此時不僅可以*(*p+2),還可以*(*p+8)=*(*(p+2)+2),只要不越二維數組的界(這里是9);
簡單點就是:int a[3][3] = {1...9};int *p = &a[0][0];此時可以從頭指到尾:頭是*p,尾是*(p+8);
2、所以二維數組a有m行n列,則在a[i][j]之前的元素個數為i * n + j —— 因為a[i][j]是第i + 1行,第j + 1列,而前i行都是在a[i][j]之前的

求數組大小:

char s[][20] = { "first", "second", "Third" };
char (*p)[20] = s;
printf("%d %d\n", sizeof(s) / 20, sizeof(p));    //3、4:因為p要找"\0",即'\0' —— {"...", "...", "...", "\0"}
//用指針數組
char *p[] = {"How", "are", "you"};
printf("%d\n", sizeof(p) / sizeof(char *));

函數

函數返回值:

printf("%d\n", func(-1));        //main函數

int func(int i)
{
    if (i == 0)
    {
        return i;
    }
    else
    {
        printf("h\n");        //這種情況,會返回printf輸出的字符個數(包括轉義符):此時return 2
    }
}

數組作為函數參數,形參數組與實參數組實際上是同一個數組,即傳址調用;數組作為實參,只需要寫數組名(無論是幾維數組)。

變量的生命周期與作用域:

#include <stdio.h>

void Incre();

int main()
{
    Incre();
    Incre();
    return 0;
}

void Incre()
{
    static int x = 1;        //靜態:第二次就輸出上次,保存的x的值;此外這還是一個局部var,因此作用域還是Incre
    printf("%d\n", ++x);
}

字符串:

字符常量與字符串常量的區別:

  • 字符串常量:1、字符串里面包含的字符是連續存儲的。2、每個字符串都有結束標記("\0"),這是一個特殊的字符,即ASCII碼為0的字符。3、C語言用字符數組(沒有字符串變量)來動態存儲字符串,例如char s[10] = {'a', 'b', '\0'}是一個字符串,而char s[10] = {'a', 'b'}不是。或者寫為char s[] = "abc"(在初始化的情況下可以省略數組大小)。存儲多個字符串(用二維數組):char s[][10] = {"星期一", "星期二"}(第一個大小可省略,由字符串的個數,即二維數組中包含數組的個數決定)。
  • 字符常量:char s = 'a'。

用指針來訪問字符串:

#include <stdio.h>

int main()
{
    char *p = NULL;
    char s[] = "abc";
    p = s;    //反過來就不行了,因為數組名相當於是一個指針常量
    printf("%c\n", *p);        //現在只是指向字符串的第一個字符
    //printf("%s\n", *p);    //這樣是不行的,應該是printf("%s\n", p);輸出的是這個指針指向字符串的某個字符以及后面剩下的字符,所以p在此時可以用s替代
    return 0;
}

輸入輸出字符串:

char s[] = "adjshd";
char s1[] = "casn";char *p = s;
char *p1 = s1;
gets(p);        //可以輸入空格
puts(p);
scanf("%s", p1);
printf("%s\n", p1);

但是gets是存在安全隱患的(它沒有限制輸入的大小,即輸入可能超出字符數組的大小,此時就可能把輸入存到了指定的字符數組之外的地方):

char s2[] = "sdfjhasjd";
char *p2 = s2;
char n = 3;
//fgets限制輸入的長度
fgets(p2, n, stdin);    //最多只能接收n - 1個字符
puts(p2);

getchar函數:

用處1:

#include <stdio.h>
#include <math.h>

int main()
{
    int n = 0;
    char c = 'c';
    int result = scanf("%d", &n);
    if(result != 1)        //輸入與scanf指定的類型不符,則不能接收到。未能成功接收的字符會暫時放在緩沖區
    {
        c = getchar();        //此時getchar就是將緩沖區的字符一次取走一個,並返回
    }
    printf("%d %c\n", n, c);
    return 0;
}

用處2:

#include <stdio.h>
#include <math.h>

int main()
{
    char c = 'c';
    //getchar直接從stdin讀取一個字符,putchar從stdout輸出
    c = getchar();
    putchar(c);
    return 0;
}

字符串處理函數:

//需要#include <string.h> -- 字符串處理函數strcmp、strlen、strcpy、strcat
strcmp(s1, s2); //兩個字符串自左向右,逐個字符相比(比較ASCII碼),直到出現不同字符或'\0'為止,此時若:s1==s2;return 0;s1 > s2;return 1;否則return -1.
strlen(s1); //返回字符串s1的長度,例如:char s1[10] = "abcde";printf("返回字符串的長度:%d,返回數組的長度%d\n", strlen(s), sizeof(s));
char source[] = "hello kit";char destination[] = "world";
puts(strcpy(destination, source));puts(destination);//source->destinations
char source[] = "hello kit";char destination[] = "world";
puts(strcat(destination, source));puts(destination);//即destination + source

函數庫:

字符處理函數#include <ctype.h>:
函數的參數皆為int,返回也為int
int isdigit(int c); //判斷是否為數字字符,例如:如果是'3',return 1;是'a',return 0;
int toupper(int c);

實用函數#include <stdlib.h>
double atoi(const char *s);
double atof(const char *s); //例如: //printf("%6.2f\n", atof("1e2"));輸出:100.00——還要算上小數點 //printf("%6.2f\n", atof("y3")); 輸出: 0.00;就算是e3也不會當成是科學計數,而是和y3一樣

枚舉類型:

#include <stdio.h>

int main()
{
    //定義枚舉類型,集合{}中的元素,是枚舉元素 —— 供枚舉變量進行選擇
    //枚舉元素的值:如果沒有指定具體值,編譯器會把第一個元素處理成0,后面元素以此+1:因此枚舉元素是常量
    //當然也可以自定義枚舉元素的值:例如指定Tuesday = 100,那么Wednesday也遵循+1的原則,因此Wednesday = 101
    enum Date { Monday, Tuesday, Wednesday };
    //定義枚舉變量
    //1、直接在定義類型時,順便把變量定義了
    //enum Date { Monday, Tuesday, Wednesday } date;
    //2、或者只定義變量
    //enum { Monday, Tuesday, Wednesday } date;
    //枚舉變量選擇具體的枚舉值
    enum Date date = Tuesday;
    date = Wednesday;
    printf("%d\n", date);
    return 0;
}

聯合union:

編譯器處理union時,一個union所有成員共用同一段內存空間(同一時刻,只有一個成員使用內存空間,存放成員值),因此內存空間的大小=最長的成員大小。具體使用如下:

#include <stdio.h>
int main()
{  
    union
    { 
        int i[2];
        int k;
        int c;
    } t, *s = &t;
    //給哪個成員賦值,內存空間就被這個成員使用
    s->i[0] = 10;
    s->i[1] = 20;
    printf("%d\n", s->c);
    
    s->k=30;
    printf("%d\n", s->c);
    
    s->c = 1000;
    printf("%d\n", s->c);
    printf("%d\n", s->i[1]);//始終為20
    return 0;
}

使用聯合的一些注意事項:

1、聯合不能作為函數參數傳遞。2、不能整體初始化或者整體賦值,只能對其成員一個一個賦值。3、不能作為函數返回值。

結構體:

#include <stdio.h>

typedef struct student
{   
    int number;
    char name[20];
    char sex;
} S;//此時S是別名
struct
{   
    int year;
    int month;
    int day;
} birth;//此時birth是結構體變量

int main()
{
    S s;
    s.number = 1000;
    birth.year = 250;
    printf("%d %d\n", birth.year, s.number);
    return 0;
}

文件操作

<stdio.h>中的一些變量類型、宏定義:

  • 文件結束符:#define EOF     (-1)
  • 存儲文件流信息的類型:typedef struct _iobuf FILE;
  • 類型的大小,即sizeof運算符的結果:typedef unsigned int size_t;

打開已存在文件、或新建一個文件:

函數原型:FILE *fopen(const char *filename, const char *mode);//用FILE的指針變量操縱文件
根據打開模式(mode)的不同決定:新建或打開文件
1、文本文件:
mode的值:r(打開已有文件,只讀)、w(打開文件,只寫、覆蓋寫入,即打開后刪除原有內容,重新寫入。若文件不存在,則新建)、a(打開文件,只寫、追加寫入。若文件不存在,則新建)
可讀寫:r+(打開已有文件,覆蓋寫入)、w+(新建文件)、a+(打開文件。若文件不存在,則新建。寫:追加寫入)
2、二進制文件mode:形如ab、ab+
=>每次讀寫都會從文件游標開始讀(第一次讀,游標在文件開頭),游標屬於fp指向的 FILE 變量:因此,對於同一個FILE變量,fgetc的調用也會影響到fgets的讀取位置

清空緩沖區數據,並關閉文件:

函數原型:int fclose(FILE *fp);
成功關閉,釋放掉文件占用的內存,return 0;發生錯誤,return EOF(常量,在stdio.h中定義);

寫入文件:

寫入一個字符:int fputc(int c, FILE *fp);
如果成功,return 剛寫入的c;如果失敗,return EOF;
寫入一個字符串:int fputs(const char *s, FILE *fp);
如果成功,return 一個非負值;失敗,return EOF;

格式化寫入文件:

//與fputs同理,函數原型:int fprintf(FILE *fp, const char *formatSting, ...);
//像printf一樣,接受可變長參數,如:
fprintf(fp, "%d != %d\n", 1, 2);//即寫入1 != 2

讀取文件:

//與寫入同理:int fgetc(FILE *fp)
printf("%c\n", fgetc(fp));
fgetc(fp);//每調用一次,讀取文件的游標,就會向后移動一個char
printf("%c\n", fgetc(fp));
//讀取字符串:char *fgets(char *buf, int n, FILE *fp);
//讀取n - 1個字符。並將讀取到的字符串,復制到字符串變量buf中,最后以\0結束 —— buf的長度應不少於n
//返回的字符串與buf的值相等
char str[7];
char *buf = str;
printf("%s\n", fgets(buf, 7, fp));//如果還未讀取n個char,就遇到了\n或者EOF,則直接結束本次讀取(包含\n)

直接讀取一個單詞(從游標開始,到遇到的第一個空格為止,認為是一個單詞;游標是讀到了這個空格的,因此調用下面的函數后,游標指向這個空格之后):

char buff[255];
//與fprintf不同:%s作為sourceString(不加修飾),buff作為destString
fscanf(fp, "%s", buff);
fscanf(fp, "%s", buff);
printf("%s\n", buff);//讀到第二個單詞

檢測當前游標是否指向文件結尾(EOF):

int feof(FILE *fp);//文件結束,return 1;否則return 0;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM