C語言深度剖析書籍學習記錄 第一章 關鍵字

C語言標準定義了32個關鍵字

union聲明聯合數據類型

  • Union declaration - cppreference.com
  • 維護足夠的空間來置放多個數據成員中的“一種”,而不是為每一個數據成員配置空間,在 union 中所有的數據成員共用一個空間,同一時間只能儲存其中一個數據成員,所有的數據成員具有相同的起始地址
union StateMachine
{char character;int number; char *str; double exp;
};
  • 一個 union 只配置一個足夠大的空間以來容納最大長度的數據成員,以上例而言,最大 長度是 double 型態,所以 StateMachine 的空間大小就是 double 數據類型的大小。
  • union主要目的是為了壓縮數據存儲的空間,如果變量不會在同一時間被使用到就可以使用 union?

enum聲明枚舉類型

  • Enumeration declaration - cppreference.com?

rigister 聲明寄存器變量

  • Storage class specifiers - cppreference.com
  • https://en.wikibooks.org/wiki/C%2B%2B_Programming/Programming_Languages/C%2B%2B/Code/Keywords/register
  • 這個關鍵字請求編譯器盡可能的將變量存在 CPU 內部寄存器中而不是通過內存尋址訪問以提高效率。注意是盡可能,不是絕對。因為 CPU 的寄存器有限
  • 注意: 寄存器在 c 和 c++之間有不同的語義。在 c 語言中,可以通過聲明數組寄存器來禁止數組到指針的轉換: register int a [1] ;?
  • 從 C++ 17 開始,auto 關鍵字不再是 C++ 存儲類說明符,且 register 關鍵字被棄用
  • 這意味著變量的最大尺寸等于寄存器的大小(通常是一個詞),且不能對它應用一元的 '&' 運算符(因為它沒有內存位置)。
  • 存儲空間分配不同,auto類型分配在棧上,屬于動態存儲類別,占動態存儲區空間,函數調用結束后自動釋放;
  • 而static分配在靜態存儲區,在程序整個運行期間都不釋放。兩者之間的作用域相同,但生存期不同
  • static局部變量在所處模塊的初次運行時進行初始化工作,且只初始化一次。
  • 對于局部靜態變量,如果不賦初值,編譯期會自動賦初值0或空字符;而auto類型的初值是不確定的。(對于C++中的class對象例外,class的對象實例如果不初始化,則會自動調用默認構造函數,不管是否是static類型)?
  • register 變量必須是 能被 CPU 寄存器所接受的類型。意味著 register 變量必須是一個單個的值,并且其長度應小 于或等于整型的長度

volatile說明變量類型可以被隱含改變

  • cv (const and volatile) type qualifiers - cppreference.com
  • 編 譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問
  • const volatile修飾的變量 - Jeremy's blog
int i=10;
int j = i;//(1)語句 
int k = i;//(2)語句
  • 這時候編譯器對代碼進行優化,因為在(1)、(2)兩條語句中,i 沒有被用作左值。這時候 編譯器認為 i 的值沒有發生改變,所以在(1)語句時從內存中取出 i 的值賦給 j 之后,這個 值并沒有被丟掉,而是在(2)語句時繼續用這個值給 k 賦值。編譯器不會生成出匯編代碼 重新從內存里取 i 的值,這樣提高了效率。但要注意:(1)、(2)語句之間 i 沒有被用作左 值才行。
volatile int i=10; 
int j = i;//(3)語句 
int k = i;//(4)語句
  • ?volatile 關鍵字告訴編譯器 i 是隨時可能發生變化的,每次使用它的時候必須從內存中取出 i 的值,因而編譯器生成的匯編代碼會重新從 i 的地址處讀取數據放在 k 中。這樣看來,如果 i 是一個寄存器變量或者表示一個端口數據或者是多個線程的共享數 據,就容易出錯,所以說 volatile 可以保證對特殊地址的穩定訪問。
    但是注意:在 VC++6.0 中,一般 Debug 模式沒有進行代碼優化,所以這個關鍵字的作 用有可能看不出來。你可以同時生成 Debug 版和 Release 版的程序做個測試。

mutable 存儲類

  • mutable?說明符僅適用于類的對象,這將在本教程的最后進行講解。它允許對象的成員替代常量。也就是說,mutable 成員可以通過 const 成員函數修改。

thread_local 存儲類

  • 使用 thread_local 說明符聲明的變量僅可在它在其上創建的線程上訪問。 變量在創建線程時創建,并在銷毀線程時銷毀。 每個線程都有其自己的變量副本。
  • thread_local 說明符可以與 static 或 extern 合并。
  • 可以將 thread_local 僅應用于數據聲明和定義,thread_local 不能用于函數聲明或定義。
  • 以下演示了可以被聲明為 thread_local 的變量:
thread_local int x;  // 命名空間下的全局變量
class X
{static thread_local std::string s; // 類的static成員變量
};
static thread_local std::string X::s;  // X::s 是需要定義的void foo()
{thread_local std::vector<int> v;  // 本地變量
}

定義

  • 所謂的定義就是(編譯器)創建一個對象,為這個對象分配一塊內存,取上一個名字,這個名字就是變量名或對象名
  • 但注意,這個名字一旦和 這塊內存匹配起來,它們就同 生共死,終生不離不棄。并且這塊內存的位置也不能被改變。
  • 一個變量或對象在一定的區 域內(比如函數內,全局等)只能被定義一次,如果定義多次,編譯器會提示你重復定義 同一個變量或對象。

聲明? 兩重含義

第一重

  • 通知編譯器,變量名字 已經匹配到一塊內存空間,此刻出現的變量或對象是在別的地方已經定義過了
  • 聲明可以出現多次

第二重

  • 通知編譯器,名字已經提前預定了,別的地方再也不能用它來作為變量名或對象名
  • 例子:void fun(int i, char c);

例子

  • int i; 定義
  • extern int i; 聲明

重點:

  • 定義聲明最重要的區別:定義創建了對象并為這個對象分配了內存,聲明沒有分配內存

基本數據類型

  • 使用sizeof 查看具體平臺數據類型的大小

命名規則

  • ?所有宏定義、枚舉常數、只讀變量全用大寫字母命名,用下劃線分割單詞。?
const int MAX_LENGTH = 100; //這不是常量,而是一個只讀變量,具體請往后看 
#define FILE_PATH “/usr/tmp”
  • 定義變量的同時千萬千萬別忘了初始化。因為,定義變量時編譯器并不一定清空了 這塊內存,它的值可能是無效的數據
  • 不同類型數據之間的運算要注意精度擴展問題,一般低精度數據將向高精度 數據擴展。

sizeof

  • sizeof(p) 和 sizeof(*p)的區別?
  • sizeof(p)是指針類型,64位平臺是8,32位平臺是4
  • sizeof(*p) 是指針指向的數據類型的大小,如果是long double就是16,double是8,int 是4
int main(){long double *p = nullptr;std::cout << sizeof(p) <<std::endl;std::cout << sizeof(*p) <<std::endl;
}
int main(){int a[100];std::cout << sizeof(a) <<std::endl;    //400std::cout << sizeof(&a) <<std::endl;   //8std::cout << sizeof(&a[0]) <<std::endl;//8
}
#include <iostream>int b[100];
void fun(int b[100]){std::cout << sizeof(b) <<std::endl;    //8
}int main(){fun(b);
}
#include <iostream>
#include <cstring>int main(){char a[1000];for (int i = 0; i < 1000; ++i) {a[i] = -1 - i;}printf("%d",strlen(a));
}
  • for 循環內,當 i 的值為 0 時,a[0]的值為-1。關鍵就是-1 在內存里面如何存儲。
  • 計算機系統中,數值一律用補碼來表示(存儲)。主要原因是使用補碼,可以將符號位和其它位統一處理;同時,減法也可按加法來處理。另外,兩個用補碼表示的數 相加時,如果最高位(符號位)有進位,則進位被舍棄正數的補碼與其原碼一致;負數的 補碼:符號位為 1,其余位為該數絕對值的原碼按位取反然后整個數加 1
    按照負數補碼的規則,可以知道-1 的補碼為 0xff,-2 的補碼為 0xfe......當 i 的值為 127 時,a[127]的值為-128,而-128 是 char 類型數據能表示的最小的負數。當 i 繼續增加,a[128] 的值肯定不能是-129。因為這時候發生了溢出,-129 需要 9 位才能存儲下來,而 char 類型 數據只有 8 位,所以最高位被丟棄。剩下的 8 位是原來 9 位補碼的低 8 位的值,即 0x7f。 當 i 繼續增加到 255 的時候,-256 的補碼的低 8 位為 0。然后當 i 增加到 256 時,-257 的補 碼的低 8 位全為 1,即低八位的補碼為 0xff,如此又開始一輪新的循環......
  • 按照上面的分析,a[0]到 a[254]里面的值都不為 0,而 a[255]的值為 0。strlen 函數是計 算字符串長度的,并不包含字符串最后的‘\0’。而判斷一個字符串是否結束的標志就是看 是否遇到‘\0’。如果遇到‘\0’,則認為本字符串結束。分析到這里,strlen(a)的值為 255 應該完全能理解了。這個問題的關鍵就是要明白 char 類型默認情況下是有符號的,其表示的值的范圍為[-128,127],超出這個范圍的值會產生溢 出。另外還要清楚的就是負數的補碼怎么表示。弄明白了這兩點,這個問題其實就很簡單了。
所謂原碼就是前面所介紹的二進制定點表示法,即最高位為符號位,“0”表示正,“1”表示負,其余位表示數值的大小。反碼表示法規定:正數的反碼與其原碼相同;負數的反碼是對其原碼逐位取反,但符號位除外。補碼表示法規定:正數的補碼與其原碼相同;負數的補碼是在其反碼的末位加1。根據原碼的定義:正零和負零的原碼為:+0 : 0000 0000 0000 0000 0000 0000 0000 0000 (32 bit)-0 : 1000 0000 0000 0000 0000 0000 0000 0000而反碼為:+0 : 0000 0000 0000 0000 0000 0000 0000 0000-0 : 1111 1111 1111 1111 1111 1111 1111 1111補碼為:+0 : 0000 0000 0000 0000 0000 0000 0000 0000-0 : 1 0000 0000 0000 0000 0000 0000 0000 0000可以看出,-0的補碼發生溢出,舍棄最高位后,其跟+0在內存的表示一樣,都是:0000 0000 0000 0000 0000 0000 0000 0000

switch case

  • case 后面只能是整型或字符型的常量或常量表達式(想想字符型數據在內存里 是怎么存的)?
  • switch case排列順序
    • 把正常情況放在前面,而把異常情況放在后面
    • 按執行頻率排列 case 語句
  • switch 里面不可以使用 continue?
  • ?在switch case 語句中能否使用continue關鍵字?_Keep Fighting All The Time-CSDN博客_switch語句中的continue

循環代碼

  • 多重循環中,如果有可能,應當將最長的循環放在最內層,最短的循環放在最外層,以減少 CPU 跨切循環層的次數。

void

  • void *可以指向任何類型的數據?
  • void 不能代表一個真實的變量,因為沒有內存空間

return關鍵字

  • c++ - Return char* from function - Stack Overflow

return char*

  • 函數內新建一個static char數組,這樣函數結束數組也不會被銷毀? ?/? ?使用const char * 字符串存儲在常量區
  • 函數內部動態申請內存,使用完需要釋放內存
  • 全局聲明數組,不好,別人會改
  • 函數返回char* 的解決方案_芒果兒-CSDN博客_c++ 返回char*

const

  • case 語句后必須是一個常量,const 修飾的只讀變量仍然是變量,所以是不可以的
  • 【C語言】const關鍵字用法 - 代碼先鋒網

  • const 定義的只讀變量從匯編的角度來看,只是給出了對應的內存地址,而不是象#define 一樣給出的是立即數,所以,const 定義的只讀變量在程序運行過程中只有一份拷貝(因為 它是全局的只讀變量,存放在靜態區),
  • 而#define 定義的宏常量在內存中有若干個拷貝。 #define 宏是在預編譯階段進行替換,而 const 修飾的只讀變量是在編譯的時候確定其值。
  • #define 宏沒有類型,而 const 修飾的只讀變量具有特定的類型。?

  • const 離哪個近,修飾哪個變量,不可以改變?

  • 修飾函數的參數? 告訴編譯器 ,形參輸入,在函數體中的不能改變,從而防止了使用者的一些無意的或錯誤的修改。?
  • 修飾函數的返回值 const 修飾符也可以修飾函數的返回值,返回值不可被改變。例如: const int Fun (void);

參考鏈接

  • What does sizeof (int) * p semantically mean?
  • c - Difference between sizeof(*p) and sizeof(p)? - Stack Overflow
  • c語言中的 %u 什么意思啊?_百度知道

struct關鍵字

  • 多種數據組合起來的一個整體,其表現形式是一個結構體
  • 傳入傳出都是結構體的形式,可以壓縮傳輸的參數
  • 結構體所占的內存大小是其成員所占內存之和? sizeof(struct),這里還涉及到結構體的內存對齊
  • 即使是空的結構體,使用sizeof求內存,其大小是1,主要是為其分配一個地址,空類也是一樣的

柔性數組

  • 結構中的最后一個元素允許是未知大小的數組,這就叫做柔性數組成員,但結構中的柔性數組成員前面必須至少一個其他成員。
  • 柔性數組成員允許結構中包含一個大小可變的數組。sizeof 返回的這種結構大小不包括柔性數組的內存。包含柔性數組成員的結構用 malloc ()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
  • 用 sizeof(type_a)得到的只有 4,就是 sizeof(i)=sizeof(int)。那個 0 個元素的數組沒有占用空間,而后我們可以進行變長操作了。使用malloc分配內存之后,使用sizeof探測整體的數據大小,仍然是 4
  • 柔性數組只是編外人員,不占結構體的編制。只是說 在使用柔性數組時需要把它當作結構體的一個成員,僅此而已。再說白點,柔性數組其實與 結構體沒什么關系,只是“掛羊頭賣狗肉”而已,算不得結構體的正式成員
  • 當然,上面既然用 malloc 函數分配了內存,肯定就需要用 free 函數來釋放內存
  • 這個柔性數組的概念 實際使用很少
#include <iostream>
#include <cstring>typedef struct st_type{int i;int a[];
}type_a;
int main(){std::cout << sizeof(type_a) << std::endl;  //4type_a * p = (type_a*) malloc(sizeof (type_a) + 100 * sizeof(int));std::cout << sizeof(type_a) << std::endl;  //4free(p);
}

?大端模式 和 小端模式

  • 大端模式(Big_endian):字數據的高字節存儲在低地址中,而字數據的低字節則存放在高地址中。
  • 小端模式(Little_endian):字數據的高字節存儲在高地址中,而字數據的低字節則存放在低地址中。

?

  • 變量i占4個字節,但只有一個字節的值為1,另外三個字節的值都為0。如果取出低地址上的值為0,毫無疑問,這是大端模式;如果取出低地址上的值為1,毫無疑問,這是小端模式。

使用程序驗證

#include <iostream>
#include <cstring>int checkSystem(){union check{int i;char ch;}c;c.i = 1;return (c.ch == 1);
}
int main(){int a = checkSystem();std::cout << a << std::endl;
}

直接查看內存

?

  • ?枚舉變量的大小,實質是常數所占內存空間的大小(常數為int類型,當前主流的編譯器中一般是32位機器和64位機器中int型都是4個字節),枚舉類型所占內存大小也是這樣。參考鏈接:enum枚舉變量所占內存大小_bulebin的博客-CSDN博客_枚舉類型大小

?

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

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

相關文章

js頁面自適應屏幕大小_移動端自適應布局方法的calc()與vw

前端人員在處理移動端自適應布局時&#xff0c;目前前端最流行的方法應該就是使用媒體查詢&#xff0c;來設置HTML的字體大小&#xff0c;然后用rem為單位對Dom的寬高進行設置&#xff0c;這個方法的優勢在于兼容性方面很好&#xff0c;劣勢則在于當前市場上不同的機型太多&…

C語言深度剖析書籍學習記錄 第二章 符號

\ 連接符號&#xff0c;// \ 可以把下一行也注釋調編譯器 刪除注釋時&#xff0c;會使用空格進行替代

詳細描述三個適于瀑布模型的項目_IT項目管理筆記——方法選擇和軟件評估

一、管理需求為什么要管理需求&#xff1f;避免失敗&#xff0c;提高項目的成功率和需求管理所帶來的其他好處軟件生命周期中&#xff0c;一個錯誤發現得越晚&#xff0c;修復錯誤的費用越高許多錯誤是潛伏的&#xff0c;并且在錯誤產生后很長一段時間才被檢查出來在需求階段&a…

Socket通信 客戶端加密數據,傳遞數據密文到服務端,服務端解密密文 輸出明文

server // sdf_cpp_warpper.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。 // server端#ifndef UNICODE #define UNICODE #endif#define WIN32_LEAN_AND_MEAN#include <iostream> #include <string> #include <sstream> #include …

主進程中發生了一個javascript錯誤_知道html5 Web Worker標準嗎?能實現JavaScript的多線程?

js為什么是單線程&#xff1f;主要是因為最開始javascript是單純的服務于瀏覽器的一種腳步語言(那時候沒有nodejs)。瀏覽器是為了渲染網頁&#xff0c;通過dom與用戶交互&#xff0c;如果一個線程需要給dom執行click事件&#xff0c;而另一個進程要刪除這個dom&#xff0c;這2個…

C語言深度剖析書籍學習記錄 第三章 預處理

宏 _LINE_ 表示正在編譯的文件的行號_FILE_ 表示正在編譯的文件的名字_DATE_ 表示編譯時刻的日期字符串&#xff0c;例如: "25 Dec 2007"_TIME_ 表示編譯時刻的時間字符串&#xff0c;例如: "12:30:55"_STDC_ 判斷該文件是不是定義成標準 C 程序宏名的書寫…

js正則限制字符串長度_正則筆記(3)萬字長文,慎點。

正則講了很久&#xff0c;也拖了很久&#xff0c;今天看看怎么用吧&#xff0c;后續更文應該會比較準勤快了。:-)書接上文【正則筆記(2)】。這次我們來看看正則的使用&#xff1a;(注&#xff1a;斜體表示為對應規則寫出的正則表達式)一、 常用的正則表達式&#xff1a;1. 驗證…

C語言深度剖析書籍學習記錄 第四章 指針和數組

p 稱為指針變量,p 里存儲的內存地址處的內存稱為 p 所指向的內存。 指針變量 p 里存儲的任何數據都將被當作地址來處理一個基本的數據類型(包括結構體等自定義類型)加上“*” 號就構成了一個指針類型的模子。這個模子的大小是一定的&#xff0c;與“*”號前面的數據類型無 關。…

js中select下拉框重置_如何利用CSS3制作炫酷的下拉框

很多小伙伴都不清楚CSS3是做什么&#xff1f;用途是什么&#xff1f;接下來我就給展示一個css3制作一個炫酷下拉框。其實不只是這些&#xff0c;還有很多。CSS3是CSS(層疊樣式表)技術的升級版本&#xff0c;于1999年開始制訂&#xff0c;2001年5月23日W3C完成了CSS3的工作草案&…

select選擇框必輸校驗_輪子這么多,我們為什么選擇自研NewSQL

作者介紹李鑫&#xff0c;滴滴資深軟件開發工程師&#xff0c;多年分布式存儲領域設計及開發經驗。曾參與NoSQL/NewSQL數據庫Fusion、分布式時序數據庫sentry、NewSQL數據庫SDB等系統的設計開發工作。一、背景Fusion-NewSQL是由滴滴自研的在分布式KV存儲基礎上構建的NewSQL存儲…

C語言深度剖析書籍學習記錄 第五章 內存管理

常見的內存錯誤 定義了指針變量&#xff0c;但是沒有為指針分配內存&#xff0c;即指針沒有指向一塊合法的內存。 結構體成員指針未初始化 很多初學者犯了這個錯誤還不知道是怎么回事。這里定義了結構體變量 stu&#xff0c;但是他沒 想到這個結構體內部 char *name 這成員在定…

怎么改電腦網絡ip地址_拋棄重啟路由器獲取ip地址方式,巧妙運用ip代理改IP工具...

網絡是簡單的也是復雜的&#xff0c;在如此龐大的網絡世界里有太多的不確定因素&#xff0c;導致我們遇到IP限制問題&#xff0c;從而影響到我們的網絡訪問&#xff0c;而大家都知道&#xff0c;如果遇到ip被限制的問題&#xff0c;最快速直接的辦法就是把被限制的ip更換一個新…

C語言深度剖析書籍學習記錄 第六章 函數

函數的好處 1、降低復雜性:使用函數的最首要原因是為了降低程序的復雜性&#xff0c;可以使用函數來隱含信息&#xff0c;從而使你不必再考慮這些信息。2、避免重復代碼段:如果在兩個不同函數中的代碼很相似&#xff0c;這往往意味著分解工作有誤。這時&#xff0c;應該把兩個…

如何把word分裝到兩個byte_如何核對兩個Word文檔的內容差別?同事加班半小時,我只花了30秒...

昨天下班前&#xff0c;老板突然發了兩份Word文檔過來&#xff0c;一份是原稿&#xff0c;還有一份是修訂稿&#xff0c;叫我們找出兩份文檔的內容差別之處&#xff0c;我只花了30秒就搞定了&#xff0c;然后準時下班&#xff01;你想知道我是怎么操作的嗎&#xff1f;下面小源…

stm32f767中文手冊_ALIENTEK 阿波羅 STM32F767 開發板資料連載第五章 SYSTEM 文件夾

1)實驗平臺&#xff1a;alientek 阿波羅 STM32F767 開發板2)摘自《STM32F7 開發指南(HAL 庫版)》關注官方微信號公眾號&#xff0c;獲取更多資料&#xff1a;正點原子第五章 SYSTEM 文件夾介紹第三章&#xff0c;我們介紹了如何在 MDK5 下建立 STM32F7 工程。在這個新建的工程之…

手機安卓學習 內核開發

官網開源代碼 Documentation - MiCode/Xiaomi_Kernel_OpenSource - Sourcegraph Xiaomi 11T Pro GitHub - MiCode/Xiaomi_Kernel_OpenSource: Xiaomi Mobile Phone Kernel OpenSourceAndroid 開源項目 | Android Open Source Project google安卓官網 目錄概覽 參考…

vs 啟動調用的目標發生異常_如何解決不可測、異常場景的問題?

阿里QA導讀&#xff1a;在軟件研發過程中&#xff0c;發布前跨多個系統的聯調測試是不可或缺的一環&#xff0c;而在聯調過程中&#xff0c;經常會遇到一些比較棘手的困難&#xff0c;阻塞整個聯調進程。其中比較典型的有&#xff1a;第三方的研發節奏不一致&#xff0c;導致無…

Linux內核 scatterlist介紹

scatterlist 物理內存的散列表。通俗講&#xff0c;就是把一些分散的物理內存&#xff0c;以列表的形式組織起來 誕生背景 假設有三個模塊可以訪問memory&#xff1a;CPU、DMA控制器和某個外設。CPU通過MMU以虛擬地址&#xff08;VA&#xff09;的形式訪問memory&#xff1b;…

www.python123.org_python爬蟲-requests

Requests庫是目前常用且效率較高的爬取網頁的庫1.一個簡單的例子import requests #引入requests庫r requests.get("http://www.baidu.com")  #調用get方法獲取界面print(r.status_code)    #輸出狀態碼print(r.text)    #輸出頁面信息通過以下代碼&#x…