內存不能為written修復工具(該內存不能read?已解決)
內存不能為written修復工具文章列表:
- 1、該內存不能read?已解決
- 2、電腦常用小知識收藏
- 3、Linux下用rm誤刪除文件的三種恢復方法
- 4、在 Linux 上創建并調試轉儲文件 | Linux 中國
- 5、C++ 標準from 11 to 20主要新增特性
該內存不能read?已解決
在我們使用電腦是時候,遇到這種情況“不能為read或者written。”
先說幾個常見的原因:
1.驅動程序不穩定,或者與操作系統、計算機硬件不兼容。
2.不小心安裝了流氓軟件或IE流氓插件;
3.正在加載的程序與已經運行的發生沖突,如有的殺毒軟件實時監控程序,會和其它殺毒軟件爭奪系統控制權,導致內存不能為read;
4.計算機感染了病毒;
5.玩游戲時出現內存不能為Read,則是因為顯卡驅動不匹配;
6.夏天時硬件過熱,散熱不好,導致內存故障,不能為Read;
7.內存條與主板兼容性問題,導致內存不能為Read。
我的解決方法:
因為我是之前裝python跟sublime的時候還不懂設置環境變量導致的錯誤。先試用了方法一仍未能解決,后面在環境變量,系統-path里面增加了以下代碼:%SystemRoot%system32;%SystemRoot%;%SystemRoot%System32Wbem。
常見方法一:
WIN R,打開電腦終端窗口輸入cmd,復制粘貼(粘貼只可鼠標右鍵,不能CTRL V)代碼for %1 in (%windir%system32*.ocx) do regsvr32 /s %1;出現以下結果:
然后再復制粘貼,for %1 in (%windir%system32*.dll) do regsvr32.exe /s %1
等待命令執行(滾動)完畢,出現以下結果:
然后關掉,重啟。
順便講一下會出現的一個情況:“不是內部命令或外部命令,也不是可運行程序”。這個可以看上一期。
方法二:拆開電腦,拔下內存條,重新插上。
看過我往期的朋友就知道我的電腦從來不用360,電腦管家一類的。所以就沒用上這些工具,電腦不輕易出現問題,網速也是相當快。
更多資訊,請關注肖大銳~
Linux下用rm誤刪除文件的三種恢復方法
對于rm,很多人都有慘痛的教訓。我也遇到一次,一下午寫的程序就被rm掉了,幸好只是一個文件,第二天很快又重新寫了一遍。但是很多人可能就不像我這么幸運了。本文收集了一些在Linux下恢復rm刪除的文件的方法,給大家作為參考。
1.幾點建議避免誤刪
首先,最好的方法是避免這個問題,以下是幾點建議:
1、rm -rf誤操作的后果是可怕的,rm -f也要三思而行,不能輕易使用。
2、做好數據備份。
3、用一些策略避免出錯:
提倡在shell下用 TAB 補全,用腳本執行任務,減少出錯的機會。或者編寫一個腳本,起名rm,在腳本里將真實的rm改為mv ,將刪除的都mv到一個指定的目錄里面,定期清理。
那么rm刪除的文件還能恢復嗎?
rm的man里面有如下說法:
請注意,如果使用 rm 來刪除文件,通常仍可以將該文件恢復原狀。如果想保證該文件的內容無法還原,請考慮使用 shred。
所以理論上rm刪除的文件是還能恢復的。刪掉文件其實只是將指向數據塊的索引點(information nodes)釋放,只要不被覆蓋,數據其實還在硬盤上,關鍵在于找出索引點,然后將其所指數據塊內的數據抓出,再保存到另外的分區。在用rm誤刪除文件后,我們要做的第一件事就是保證不再向誤刪文件的分區寫數據。
2.使用lsof命令恢復
lsof命令用于查看你進程開打的文件,打開文件的進程,進程打開的端口(TCP、UDP)。找回/恢復刪除的文件。是十分方便的系統監視工具,因為lsof命令需要訪問核心內存和各種文件,所以需要root用戶執行。
在linux環境下,任何事物都以文件的形式存在,通過文件不僅僅可以訪問常規數據,還可以訪問網絡連接和硬件。所以如傳輸控制協議 (TCP) 和用戶數據報協議 (UDP) 套接字等,系統在后臺都為該應用程序分配了一個文件描述符,無論這個文件的本質如何,該文件描述符為應用程序與基礎操作系統之間的交互提供了通用接口。因為應用程序打開文件的描述符列表提供了大量關于這個應用程序本身的信息,因此通過lsof工具能夠查看這個列表對系統監測以及排錯將是很有幫助的。
1.語法
lsof(選項)
2.參數
-a:列出打開文件存在的進程; -c<進程名>:列出指定進程所打開的文件; -g:列出GID號進程詳情; -d<文件號>:列出占用該文件號的進程; d<目錄>:列出目錄下被打開的文件; D<目錄>:遞歸列出目錄下被打開的文件; -n<目錄>:列出使用NFS的文件; -i<條件>:列出符合條件的進程。(4、6、協議、:端口、 @ip ) -p<進程號>:列出指定進程號所打開的文件; -u:列出UID號進程詳情; -h:顯示幫助信息; -v:顯示版本信息。
3.使用
查看
lsof -i:(端口) 查看這個端口有那些進程在訪問,比如22端口
shell> lsof -i:22COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsshd 1939 root 3u IPv4 12317 0t0 TCP *:ssh (LISTEN)sshd 1939 root 4u IPv6 12321 0t0 TCP *:ssh (LISTEN)sshd 2790 root 3u IPv4 15229 0t0 TCP 192.168.178.128:ssh->192.168.178.1:64601 (ESTABLISHED)sshd 2824 root 3u IPv4 15528 0t0 TCP 192.168.178.128:ssh->192.168.178.1:64673 (ESTABLISHED)sshd 2990 root 3u IPv4 15984 0t0 TCP 192.168.178.128:ssh->192.168.178.1:64686 (ESTABLISHED)sshd 14695 root 3u IPv4 39558 0t0 TCP 192.168.178.128:ssh->192.168.178.1:49662 (ESTABLISHED)
lsof輸出各列信息的意義如下:
COMMAND:進程的名稱
PID:進程標識符
USER:進程所有者
FD:文件描述符,應用程序通過文件描述符識別該文件。如cwd、txt等
TYPE:文件類型,如DIR、REG等
DEVICE:指定磁盤的名稱
SIZE:文件的大小
NODE:索引節點(文件在磁盤上的標識)
NAME:打開文件的確切名稱
恢復文件
利用lsof可以恢復一些系統日志,前提是這個進程必須存在。這里就拿最常用的/var/log/messages來舉例說明,大家在做測試的時候最好先備份一下。
#備份shell> cp /var/log/message /var/log/message_bacshell> lsof |grep /var/log/messagersyslogd 1737 root 1w REG 8,2 5716123 652638 /var/log/messages
進程在運行中,接下來我就把/var/log/messages這個文件刪掉
shell> rm /var/log/messages
刪掉之后,我再來看看這個進程的變化
shell> lsof |grep /var/log/messagesrsyslogd 1737 root 1w REG 8,2 5716123 652638 /var/log/messages (deleted)
大家看到有變化了吧, 對比兩個之后發現多了(deleted)。要找到這個文件在哪還要看看這個
PID:1737 FD:1 那我們有直接進入/proc/1737/FD/1用ll查看一下
shell> cd /proc/1737/fd/shell> lltotal 0lrwx------ 1 root root 64 Dec 23 13:00 0 -> socket:[11442]l-wx------ 1 root root 64 Dec 23 13:00 1 -> /var/log/messages (deleted)l-wx------ 1 root root 64 Dec 23 13:00 2 -> /var/log/securelr-x------ 1 root root 64 Dec 23 13:00 3 -> /proc/kmsgl-wx------ 1 root root 64 Dec 23 13:00 4 -> /var/log/maillog
看到了1對應/var/log/messages (deleted),看看文件是不是我們要的文件:
shell> head -5 1Nov 14 03:11:11 localhost kernel: imklog 5.8.10, log source = /proc/kmsg started.Nov 14 03:11:11 localhost rsyslogd: [origin software="rsyslogd" swVersion="5.8.10" x-pid="1241" x-info="http://www.rsyslog.com"] startNov 14 03:11:11 localhost kernel: Initializing cgroup subsys cpusetNov 14 03:11:11 localhost kernel: Initializing cgroup subsys cpuNov 14 03:11:11 localhost kernel: Linux version 2.6.32-431.el6.x86_64 (mockbuild@c6b8.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Fri Nov 22 03:15:09 UTC 2013
對比備份文件:
shell> head -5 /var/log/message_bacNov 14 03:11:11 localhost kernel: imklog 5.8.10, log source = /proc/kmsg started.Nov 14 03:11:11 localhost rsyslogd: [origin software="rsyslogd" swVersion="5.8.10" x-pid="1241" x-info="http://www.rsyslog.com"] startNov 14 03:11:11 localhost kernel: Initializing cgroup subsys cpusetNov 14 03:11:11 localhost kernel: Initializing cgroup subsys cpuNov 14 03:11:11 localhost kernel: Linux version 2.6.32-431.el6.x86_64 (mockbuild@c6b8.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Fri Nov 22 03:15:09 UTC 2013
對比發現數據是一樣的,恢復
shell> cat 1 > /var/log/messages
再次提醒,恢復前提是這個進程必須存在。
3.使用extundelete工具
extundelete工具安裝
extundelete下載地址:http://extundelete.sourceforge.net/
wget https://nchc.dl.sourceforge.net/project/extundelete/extundelete/0.2.4/extundelete-0.2.4.tar.bz2
解壓該文件tar jxvf extundelete-0.2.4.tar.bz2
若報這種錯誤
[root@docking ~]# tar jxvf extundelete-0.2.4.tar.bz2 tar (child): bzip2:無法 exec: 沒有那個文件或目錄tar (child): Error is not recoverable: exiting nowtar: Child returned status 2tar: Error is not recoverable: exiting now
則使用yum -y install bzip2進行解決
[root@docking ~]# tar jxvf extundelete-0.2.4.tar.bz2 extundelete-0.2.4/extundelete-0.2.4/acinclude.m4extundelete-0.2.4/missingextundelete-0.2.4/autogen.shextundelete-0.2.4/aclocal.m4extundelete-0.2.4/configureextundelete-0.2.4/LICENSEextundelete-0.2.4/README...................................................
cd extundelete-0.2.4./configure
若這步驟報錯
[root@docking extundelete-0.2.4]# ./configure Configuring extundelete 0.2.4configure: error: in `/root/extundelete-0.2.4':configure: error: C compiler cannot create executablesSee `config.log' for more details
則使用yum -y install gcc-c 解決.
若執行上一步仍然報錯,
[root@docking extundelete-0.2.4]# ./configure Configuring extundelete 0.2.4configure: error: Can't find ext2fs library
則使用yum -y install e2fsprogs e2fsprogs-devel來解決。
#Ubuntu的解決辦法為sudo apt-get install e2fslibs-dev e2fslibs-dev
不出意外的話到這里應該configure能夠順利完成.
[root@docking extundelete-0.2.4]# ./configure Configuring extundelete 0.2.4Writing generated files to disk[root@docking extundelete-0.2.4]#
最后make然后 make install
[root@docking extundelete-0.2.4]# makemake -s all-recursiveMaking all in srcextundelete.cc: 在函數‘ext2_ino_t find_inode(ext2_filsys, ext2_filsys, ext2_inode*, std::string, int)’中:extundelete.cc:1272:29: 警告:在 {} 內將‘search_flags’從‘int’轉換為較窄的類型‘ext2_ino_t {aka unsigned int}’ [-Wnarrowing] buf, match_name2, priv, 0}; ^[root@docking extundelete-0.2.4]# make installMaking install in src /usr/bin/install -c extundelete '/usr/local/bin'
extundelete安裝完成.
掃描誤刪除的文件:
使用df -lh查看掛載:
taroballs@taroballs-PC:~$ df -lh文件系統 容量 已用 可用 已用% 掛載點udev 1.9G 0 1.9G 0% /devtmpfs 387M 1.8M 385M 1% /run/dev/sda2 92G 61G 26G 71% /tmpfs 1.9G 49M 1.9G 3% /dev/shmtmpfs 5.0M 4.0K 5.0M 1% /run/locktmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup/dev/sda3 104G 56G 44G 57% /hometmpfs 387M 40K 387M 1% /run/user/1000/dev/sda4 70G 20G 47G 30% /media/taroballs/d8423f8c-d687-4c03-a7c8-06a7fb57f96d/dev/sdb1 6.8G 4.1G 2.8G 60% /media/taroballs/taroballs/dev/sr0 4.0G 4.0G 0 100% /media/taroballs/2018-01-16-12-36-00-00taroballs@taroballs-PC:~$ cd /media/taroballs/taroballs/taroballs@taroballs-PC:/media/taroballs/taroballs$
可以看到,我們的目錄/media/taroballs/taroballs
掛載到/dev/sdb1 這個文件系統中.
umount我們的掛載盤
比如:
taroballs@taroballs-PC:~$ df -lh | grep /dev/sdb1/dev/sdb1 6.8G 4.1G 2.8G 60% /media/taroballs/taroballs
umount這個目錄
taroballs@taroballs-PC:~$ umount /media/taroballs/taroballstaroballs@taroballs-PC:~$ df -lh | grep /dev/sdb1taroballs@taroballs-PC:~$ #記得刪除一定要后umount哦,不然二次寫入誰也幫不了你呢。
通過inode節點恢復
taroballs@taroballs-PC:~$ mkdir recovertesttaroballs@taroballs-PC:~$ cd recovertest/taroballs@taroballs-PC:~/recovertest$
執行恢復extundelete /dev/sdb1 --inode 2
taroballs@taroballs-PC:/media/taroballs/taroballs$ sudo extundelete /dev/sdb1 --inode 2NOTICE: Extended attributes are not restored.Loading filesystem metadata ... 8 groups loaded.Group: 0Contents of inode 2: ..省略N行 File name | Inode number | Deleted status. 2.. 2deletetest 12 Deletedtmppasswd 14 Deleted
通過掃描發現了我們刪除的文件夾,現在執行恢復操作。
(1)恢復單一文件tmppasswd
taroballs@taroballs-PC:~/recovertest$ extundelete /dev/sdb1 --restore-file passwd NOTICE: Extended attributes are not restored.Loading filesystem metadata ... 8 groups loaded.Loading journal descriptors ... 46 descriptors loaded.Successfully restored file tmppasswd
恢復文件是放到了當前目錄RECOVERED_FILES。
查看恢復的文件:
taroballs@taroballs-PC:~/recovertest$ cat tmppasswd tcpdump:x:172:72::/:/sbin/nologin
(2)恢復目錄deletetest
extundelete /dev/sdb1 --restore-directory deletetestNOTICE: Extended attributes are not restored.Loading filesystem metadata ... 8 groups loaded.Loading journal descriptors ... 46 descriptors loaded.Searching for recoverable inodes in directory deletetest ... 5 recoverable inodes found.Looking through the directory structure for deleted files ...
(3)恢復所有
taroballs@taroballs-PC:~/recovertest$ extundelete /dev/sdb1 --restore-allNOTICE: Extended attributes are not restored.Loading filesystem metadata ... 8 groups loaded.Loading journal descriptors ... 46 descriptors loaded.Searching for recoverable inodes in directory / ... 5 recoverable inodes found.Looking through the directory structure for deleted files ... 0 recoverable inodes still lost. taroballs@taroballs-PC:~/recovertest$ tree backuptest/├── deletetest│ └── innerfolder│ └── deletefile.txt└── tmppasswd2 directories, 2 files
(4)恢復指定inode
taroballs@taroballs-PC:~/recovertest$ extundelete /dev/sdb1 --restore-inode 14NOTICE: Extended attributes are not restored.Loading filesystem metadata ... 8 groups loaded.Loading journal descriptors ... 46 descriptors loaded.taroballs@taroballs-PC:~/recovertest$ cat file.14 tcpdump:x:172:72::/:/sbin/nologin#注意恢復inode的時候,恢復 出來的文件名和之前不一樣,需要單獨進行改名。
最后附上extundelete的用法:
$ extundelete --helpUsage: extundelete [options] [--] device-fileOptions: --version, -[vV] Print version and exit successfully. --help, Print this help and exit successfully. --superblock Print contents of superblock in addition to the rest. If no action is specified then this option is implied. --journal Show content of journal. --after dtime Only process entries deleted on or after 'dtime'. --before dtime Only process entries deleted before 'dtime'.Actions: --inode ino Show info on inode 'ino'. --block blk Show info on block 'blk'. --restore-inode ino[,ino,...] Restore the file(s) with known inode number 'ino'. The restored files are created in ./RECOVERED_FILES with their inode number as extension (ie, file.12345). --restore-file 'path' Will restore file 'path'. 'path' is relative to root of the partition and does not start with a '/' The restored file is created in the current directory as 'RECOVERED_FILES/path'. --restore-files 'path' Will restore files which are listed in the file 'path'. Each filename should be in the same format as an option to --restore-file, and there should be one per line. --restore-directory 'path' Will restore directory 'path'. 'path' is relative to the root directory of the file system. The restored directory is created in the output directory as 'path'. --restore-all Attempts to restore everything. -j journal Reads an external journal from the named file. -b blocknumber Uses the backup superblock at blocknumber when opening the file system. -B blocksize Uses blocksize as the block size when opening the file system. The number should be the number of bytes. --log 0 Make the program silent. --log filename Logs all messages to filename.--log D1=0,D2=filename Custom control of log messages with comma-separated Examples below: list of options. Dn must be one of info, warn, or --log info,error error. Omission of the '=name' results in messages --log warn=0 with the specified level to be logged to the console. --log error=filename If the parameter is '=0', logging for the specified level will be turned off. If the parameter is '=filename', messages with that level will be written to filename. -o directory Save the recovered files to the named directory. The restored files are created in a directory named 'RECOVERED_FILES/' by default.
在 Linux 上創建并調試轉儲文件 | Linux 中國
了解如何處理轉儲文件將幫你找到應用中難以重現的 bug。
? 來源:linux.cn ? 作者:Stephan Avenwedde ? 譯者:Xingyu.Wang ?
(本文字數:5501,閱讀時長大約:6 分鐘)
崩潰轉儲、內存轉儲、核心轉儲、系統轉儲……這些全都會產生同樣的產物:一個包含了當應用崩潰時,在那個特定時刻應用的內存狀態的文件。
這是一篇指導文章,你可以通過克隆示例的應用倉庫來跟隨學習:
git clone https://github.com/hANSIc99/core_dump_example.git
信號如何關聯到轉儲
信號是操作系統和用戶應用之間的進程間通訊。Linux 使用 POSIX 標準 中定義的信號。在你的系統上,你可以在 /usr/include/bits/signum-generic.h 找到標準信號的定義。如果你想知道更多關于在你的應用程序中使用信號的信息,這有一個信息豐富的 signal 手冊頁 。簡單地說,Linux 基于預期的或意外的信號來觸發進一步的活動。
當你退出一個正在運行的應用程序時,應用程序通常會收到 SIGTERM 信號。因為這種類型的退出信號是預期的,所以這個操作不會創建一個內存轉儲。
以下信號將導致創建一個轉儲文件(來源: GNU C庫 ):
SIGFPE:錯誤的算術操作
SIGILL:非法指令
SIGSEGV:對存儲的無效訪問
SIGBUS:總線錯誤
SIGABRT:程序檢測到的錯誤,并通過調用 abort() 來報告
SIGIOT:這個信號在 Fedora 上已經過時,過去在 PDP-11 上用 abort() 時觸發,現在映射到 SIGABRT
創建轉儲文件
導航到 core_dump_example 目錄,運行 make,并使用 -c1 開關執行該示例二進制:
./coredump -c1
該應用將以狀態 4 退出,帶有如下錯誤:
Dump written
“Abgebrochen (Speicherabzug geschrieben) ”(LCTT 譯注:這是德語,應該是因為本文作者系統是德語環境)大致翻譯為“分段故障(核心轉儲)”。
是否創建核心轉儲是由運行該進程的用戶的資源限制決定的。你可以用 ulimit 命令修改資源限制。
檢查當前創建核心轉儲的設置:
ulimit -c
如果它輸出 unlimited,那么它使用的是(建議的)默認值。否則,用以下方法糾正限制:
ulimit -c unlimited
要禁用創建核心轉儲,可以設置其大小為 0:
ulimit -c 0
這個數字指定了核心轉儲文件的大小,單位是塊。
什么是核心轉儲?
內核處理核心轉儲的方式定義在:
/proc/sys/kernel/core_pattern
我運行的是 Fedora 31,在我的系統上,該文件包含的內容是:
/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h
這表明核心轉儲被轉發到 systemd-coredump 工具。在不同的 Linux 發行版中,core_pattern 的內容會有很大的不同。當使用 systemd-coredump 時,轉儲文件被壓縮保存在 /var/lib/systemd/coredump 下。你不需要直接接觸這些文件,你可以使用 coredumpctl。比如說:
coredumpctl list
會顯示系統中保存的所有可用的轉儲文件。
使用 coredumpctl dump,你可以從最后保存的轉儲文件中檢索信息:
[stephan@localhost core_dump_example]$ ./coredump Application started…(…….)Message: Process 4598 (coredump) of user 1000 dumped core.Stack trace of thread 4598:#0 0x00007f4bbaf22625 __GI_raise (libc.so.6)#1 0x00007f4bbaf0b8d9 __GI_abort (libc.so.6)#2 0x00007f4bbaf664af __libc_message (libc.so.6)#3 0x00007f4bbaf6da9c malloc_printerr (libc.so.6)#4 0x00007f4bbaf6f49c _int_free (libc.so.6)#5 0x000000000040120e n/a (/home/stephan/Dokumente/core_dump_example/coredump)#6 0x00000000004013b1 n/a (/home/stephan/Dokumente/core_dump_example/coredump)#7 0x00007f4bbaf0d1a3 __libc_start_main (libc.so.6)#8 0x000000000040113e n/a (/home/stephan/Dokumente/core_dump_example/coredump)Refusing to dump core to tty (use shell redirection or specify?—?output).
這表明該進程被 SIGABRT 停止。這個視圖中的堆棧跟蹤不是很詳細,因為它不包括函數名。然而,使用 coredumpctl debug,你可以簡單地用調試器(默認為 GDB )打開轉儲文件。輸入 bt( 回溯(backtrace)的縮寫)可以得到更詳細的視圖:
Core was generated by `./coredump -c1'.Program terminated with signal SIGABRT, Aborted.#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:5050 return ret;(gdb) bt#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50#1 0x00007fc37a9aa8d9 in __GI_abort () at abort.c:79#2 0x00007fc37aa054af in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fc37ab14f4b "%sn") at ../sysdeps/posix/libc_fatal.c:181#3 0x00007fc37aa0ca9c in malloc_printerr (str=str@entry=0x7fc37ab130e0 "free(): invalid pointer") at malloc.c:5339#4 0x00007fc37aa0e49c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:4173#5 0x000000000040120e in freeSomething(void*) ()#6 0x0000000000401401 in main ()
與后續幀相比,main() 和 freeSomething() 的內存地址相當低。由于共享對象被映射到虛擬地址空間末尾的區域,可以認為 SIGABRT 是由共享庫中的調用引起的。共享對象的內存地址在多次調用之間并不是恒定不變的,所以當你看到多次調用之間的地址不同時,完全可以認為是共享對象。
堆棧跟蹤顯示,后續的調用源于 malloc.c,這說明內存的(取消)分配可能出了問題。
在源代碼中,(即使沒有任何 C 知識)你也可以看到,它試圖釋放一個指針,而這個指針并沒有被內存管理函數返回。這導致了未定義的行為,并導致了 SIGABRT。
void freeSomething(void *ptr){ free(ptr);}int nTmp = 5;int *ptrNull = &nTmp;freeSomething(ptrNull);
systemd 的這個 coredump 工具可以在 /etc/systemd/coredump.conf 中配置。可以在 /etc/systemd/systemd-tmpfiles-clean.timer 中配置輪換清理轉儲文件。
你可以在其 手冊頁 中找到更多關于 coredumpctl 的信息。
用調試符號編譯
打開 Makefile 并注釋掉第 9 行的最后一部分。現在應該是這樣的:
CFLAGS =-Wall -Werror -std=c 11 -g
-g 開關使編譯器能夠創建調試信息。啟動應用程序,這次使用 -c2 開關。
./coredump -c2
你會得到一個浮點異常。在 GDB 中打開該轉儲文件:
coredumpctl debug
這一次,你會直接被指向源代碼中導致錯誤的那一行:
Reading symbols from /home/stephan/Dokumente/core_dump_example/coredump…[New LWP 6218]Core was generated by `./coredump -c2'.Program terminated with signal SIGFPE, Arithmetic exception.#0 0x0000000000401233 in zeroDivide () at main.cpp:2929 nRes = 5 / nDivider;(gdb)
鍵入 list 以獲得更好的源代碼概覽:
(gdb) list24 int zeroDivide(){25 int nDivider = 5;26 int nRes = 0;27 while(nDivider > 0){28 nDivider--;29 nRes = 5 / nDivider;30 }31 return nRes;32 }
使用命令 info locals 從應用程序失敗的時間點檢索局部變量的值:
(gdb) info localsnDivider = 0nRes = 5
結合源碼,可以看出,你遇到的是零除錯誤:
nRes = 5 / 0
結論
了解如何處理轉儲文件將幫助你找到并修復應用程序中難以重現的隨機錯誤。而如果不是你的應用程序,將核心轉儲轉發給開發人員將幫助她或他找到并修復問題。
via: opensource.com
作者: Stephan Avenwedde 選題: lujun9972 譯者: wxy 校對: wxy
本文由 LCTT 原創編譯, Linux中國 榮譽推出
點擊“了解更多”可訪問文內鏈接
標準from 11 to 20主要新增特性'>C 標準from 11 to 20主要新增特性C is an ISO standardized programing language. There are different C standards:
- C 98- C 03- C 11 modern C - C 14 |- C 17 |- C 20 /
Everything starting with C 11 is referred to as “Modern C ”. These standards define the language in great technical detail. They also serve as manuals for C compiler writers. It is a mind-boggling set of rules and specifications. The C standards can be bought, or a draft version can be downloaded for free. These drafts closely resemble the final C standard. When C code can be successfully transferred and compiled on different platforms (machines or compilers), and when C implementation closely follows the standard, we say that the code is portable. This is often referred to as portable C .
The standards surrounded by braces represent the so-called “Modern C .” Each standard describes the language and introduces new language and library features. It may also introduce changes to the existing rules. We will describe notable features in each of these standards.
標準和新特性 | 解釋或描述 |
C 11 | |
11.1 Automatic Type Deduction | auto |
11.2 Range-based Loops | for(auto el:range) |
11.?3 Initializer Lists | {} |
11.?4 Move Semantics | move |
11.?5 Lambda Expressions | [](){} |
11.?6 The constexpr Specifier | constexpr |
11.?7 Scoped enumerators | enum class |
11.?8 Smart Pointers | unique_ptr |
11.?9 std:?:?unordered_?set | unordered_?set |
11.?10 std:?:?unordered_?map | ?unordered_?map |
11.?11 std:?:?tuple | ?pair<>->tuple<> |
11.?12 static_?assert | constexpr |
11.?13 Concurrency | thread(func) |
11.?14 Deleted and Defaulted Functions | default, delete |
11.?15 Type Aliases | using id = type |
C 14 | |
14.?1 Binary Literals | 0b1010 |
14.?2 Digits Separators | 123'456 |
14.?3 Auto for Functions | auto func(); |
14.?4 Generic Lambdas | auto lambdas |
14.?5 std:?:?make_?unique | make_unique() |
C 17 | |
17.?1 Nested Namespaces | n::m::p declaration |
17.?2 Constexpr Lambdas | Constexpr Lambdas |
17.?3 Structured Bindings | auto[] = arr |
17.?4 std:?:?filesystem | filesystem |
17.?5 std:?:?string_?view | non-owning view |
17.?6 std:?:?any | any c = 123; |
17.?7 std:?:?variant | union->variant |
C 20 | |
20.?1 Modules | export and import |
20.?2 Concepts | template type requires |
20.?3 Lambda Templates | []<>(){} |
20.?4 [likely] and [unlikely] Attributes | if (choice) [[likely]] |
20.?5 Ranges | ranges::sort(vec); |
20.?6 Coroutines | co_await |
20.?7 std:?:?span | span<int> is = vec; |
20.?8 Mathematical Constants | numbers::log2e |
1 C 11
C 11 is an ISO C standard, published in 2011. To compile for this standard, add the -std=c 11 flag to a command-line compilation string if compiling with g or clang. If using Visual Studio, choose Project / Options / Configuration Properties / C/C / Language / C Language Standard and choose C 11. New Visual Studio versions already support this standard out of the box.
11.1 Automatic Type Deduction
This standard introduces the auto keyword which deduces the type of the variable based on the variable’s initializer:
int main(){ auto mychar = 'A'; auto myint = 123 456; auto mydouble = 456.789;}
11.2 Range-based Loops
The range-based loops allow us to iterate over the range, such as C standard-library containers:
#include <iostream>#include <vector>int main(){ std::vector<int> v = { 10, 20, 40, 5, -20, 75 }; for (auto el : v) { std::cout << el << 'n'; }}
The range-based for loop is of the following form: for (type element : container). This is read as for each element in a container (do something).
11.3 Initializer Lists
Initializer lists, represented by braces { } allow us to initialize objects in a uniform way. We can initialize single objects:
int main(){ int x{ 123 }; int y = { 456 }; double d{ 3.14 };}
And containers:
#include <vector>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 };}
List initialization also prevents narrowing conversions. If we tried to initialize our integer object with a double value inside the initializer list, the compilation would fail:
int main(){ int x = { 123.45 }; // Error, does not allowing narrowing}
When initializing our objects, we should prefer initializer lists {} to old-style parentheses ().
11.4 Move Semantics
C 11 standard introduces the move semantics for classes. We can initialize our objects by moving the data from other objects. This is achieved through move constructors and move assignment operators. Both accept the so-called rvalue reference as an argument. Lvalue is an expression that can be used on the left-hand side of the assignment operation. rvalues are expressions that can be used on the right-hand side of an assignment. The rvalue reference has the signature of some_type&&. To cast an expression to an rvalue reference, we use the std::move function. A simple move constructor and move assignment signature are:
class MyClass{public: MyClass(MyClass&& otherobject) // move constructor { //implement the move logic here } MyClass& operator=(MyClass&& otherobject) // move assignment operator { // implement the copy logic here return *this; }};
11.5 Lambda Expressions
Lambda expressions are anonymous function objects. They allow us to write a short code snippet to be used as a standard-library function predicate. Lambdas have a capture list, marked by [ ] where we can capture local variables by reference or copy, parameter list with optional parameters marked with ( ), and a lambda body, marked with { }. An empty lambda looks like [] () {};. A simple example of counting only the even numbers in a set using the lambda as a predicate:
#include <iostream>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 }; auto counteven = std::count_if(std::begin(v), std::end(v), [](int x) {return x % 2 == 0; }); // lambda expression std::cout << "The number of even vector elements is: " << counteven;}
11.6 The constexpr Specifier
The constexpr specifier promises the variable or a function can be evaluated during compile-time. If the expression can not be evaluated during compile-time, the compiler emits an error:
int main(){ constexpr int n = 123; //OK, 123 is a compile-time constant // expression constexpr double d = 456.78; //OK, 456.78 is a compile-time constant // expression constexpr double d2 = d; //OK, d is a constant expression int x = 123; constexpr int n2 = x; //compile-time error // the value of x is not known during // compile-time}
11.7 Scoped Enumerators
Enumerator使用時(右值)需顯式聲明類作用域。
The C 11 standard introduces the scoped enumerators . Unlike the old enumerators, the scoped enumerators do not leak their names into the surrounding scope. Scoped enums have the following signature: enum class Enumerator_Name {value1, value2 etc} signature. A simple example of a scoped enum is:
enum class MyEnum{ myfirstvalue, mysecondvalue, mythirdvalue};int main(){ MyEnum myenum = MyEnum::myfirstvalue;}
11.8 Smart Pointers
智能指針使用類模板封裝原生指針及類指針操作及在自定義析構函數中在適當的時機調用delete或delete[](包括申請的堆內存的所有權控制或引用計數)。
Smart pointers point to objects, and when the pointer goes out of scope, the object gets destroyed. This makes them smart in the sense that we do not have to worry about manual deallocation of allocated memory. The smart pointers do all the heavy lifting for us.
There are two kinds of smart pointers, the unique pointer with an std::unique_ptr signature and a shared pointer with an std::shared_ptr signature. The difference between the two is that we can have only one unique pointer pointing at the object. In contrast, we can have multiple shared pointers pointing at an object. When the unique pointer goes out of scope, the object gets destroyed, and the memory is deallocated. When the last of the shared pointers pointing at our object goes out of scope, the object gets destroyed. The memory gets deallocated.
A unique pointer example:
#include <iostream>#include <memory>int main(){ std::unique_ptr<int> p(new int{ 123 }); std::cout << *p;} // p goes out of scope here, the memory gets deallocated, the object gets // destroyed39
A unique pointer can not be copied, only moved. To have multiple shared pointers pointing at the same object, we would write:
#include <iostream>#include <memory>int main(){ std::shared_ptr<int> p1(new int{ 123 }); std::shared_ptr<int> p2 = p1; std::shared_ptr<int> p3 = p1;} // when the last shared pointer goes out of scope, the memory gets // deallocated39
Shared pointers can be copied. It is said they share ownership of the object. When the last shared pointer gets out of scope, the pointed-to object gets destroyed, and the memory gets deallocated.
11.9 std::unordered_set
std::set使用紅黑樹為底層數據結構,std::unordered_set以哈希映射實現物理存儲。
The std::unordered_set is a container that allows for constant time insertion, searching, and removal of elements. This container is implemented as an array of buckets of linked lists. The hash value of each element is calculated (hashed), and the object is placed into an appropriate bucket based on the hash value. The object themselves are not sorted in any particular order. To define an unordered set, we need to include the header. Example:
#include <iostream>#include <unordered_set>int main(){ std::unordered_set<int> myunorderedset = { 1, 2, 5, -4, 7, 10 }; for (auto el : myunorderedset) { std::cout << el << 'n'; }}
The values are not sorted but are unique. To insert single or multiple values into an unordered_set, we use the .insert() member function:
#include <iostream>#include <unordered_set>int main(){ std::unordered_set<int> myunorderedset = { 1, 2, 5, -4, 7, 10 }; myunorderedset.insert(6); // insert a single value myunorderedset.insert({ 8, 15, 20 }); // insert multiple values for (auto el : myunorderedset) { std::cout << el << 'n'; }}
To delete a value from an unordered set, we use the .erase() member function:
#include <iostream>#include <unordered_set>int main(){ std::unordered_set<int> myunorderedset = { 1, 2, 5, -4, 7, 10 }; myunorderedset.erase(-4); // erase a single value for (auto el : myunorderedset) { std::cout << el << 'n'; }}
11.10 std::unordered_map
std::map使用紅黑樹為底層數據結構,std::unordered_map以哈希映射實現物理存儲。
Similar to std::unordered_set, there is also an std::unordered_map , an unordered container of key-value pairs with unique keys. This container also allows for fast insertion, searching, and removal of elements. The container is also data is also implemented as buckets. What element goes into what bucket depends on the element’s key hash value. To define an unordered map, we include the header. Example:
#include <iostream>#include <unordered_map>int main(){ std::unordered_map<char, int> myunorderedmap = { {'a', 1}, {'b', 2}, {'c', 5} }; for (auto el : myunorderedmap) { std::cout << el.first << ' '<< el.second << 'n'; }}
Here we initialize an unordered map with key-value pairs. In the range-based for loop, we print both the key and the value. Map elements are pairs. Pairs have member functions .first for accessing a key and .second for accessing a value. To insert an element into a map we can use the member function .insert() member function:
#include <iostream>#include <unordered_map>int main(){ std::unordered_map<char, int> myunorderedmap = { {'a', 1}, {'b', 2}, {'c', 5} }; myunorderedmap.insert({ 'd', 10 }); for (auto el : myunorderedmap) { std::cout << el.first << ' '<< el.second << 'n'; }}
We can also use the map’s operator [] to insert an element. Normally, this operator is used to access an element value by key. However, if the key does not exist, the operator inserts a new element into the map:
#include <iostream>#include <unordered_map>int main(){ std::unordered_map<char, int> myunorderedmap = { {'a', 1}, {'b', 2}, {'c', 5} }; myunorderedmap['b'] = 4; // key exists, change the value myunorderedmap['d'] = 10; // key does not exist, insert the new element for (auto el : myunorderedmap) { std::cout << el.first << ' ' << el.second << 'n'; }}
11.11 std::tuple
std::pair是一個兩個成員特殊命名(first, second)的類模板。
std::tuple是一個有多個數據成員,可以由get<>()訪問的類模板。
While std::pair can hold only two values, the std::tuple wrapper can hold more than two values. To use tuples, we need to include the header. To access a certain tuple element , we use the std::get(tuple_name) function:
#include <iostream>#include <utility>#include <tuple>int main(){ std::tuple<char, int, double> mytuple = { 'a', 123, 3.14 }; std::cout << "The first element is: " << std::get<0>(mytuple) << 'n'; std::cout << "The second element is: " << std::get<1>(mytuple) << 'n'; std::cout << "The third element is: " << std::get<2>(mytuple) << 'n';}
We can create a tuple using the std::make_tuple function:
#include <iostream>#include <tuple>#include <string>int main(){ auto mytuple = std::make_tuple<int, double, std::string>(123, 3.14, "Hello World."); std::cout << "The first tuple element is: " << std::get<0>(mytuple) << 'n'; std::cout << "The second tuple element is: " << std::get<1>(mytuple) << 'n'; std::cout << "The third tuple element is: " << std::get<2>(mytuple) << 'n';}
Instead of typing a lengthy tuple type, which is std::tuple, we used the auto specifier to deduce the type name for us.
40.1.12 static_assert
The static_assert directive checks a static (constexpr) condition during compile time. If the condition is false, the directive fails the compilation and displays an error message. Example:
int main(){ constexpr int x = 123; static_assert(x == 456, "The constexpr value is not 456.");}
Here the static_assert checks if the value of x is equal to 456 during compile time. Since it is not, the compilation will fail with a "The constexpr value is not 456." message. We can think of the static_assert as a way of testing our code during compile time. It is also a neat way of testing if the value of a constexpr expression is what we expect it to be.
40.1.13 Introduction to Concurrency
C 11 standard introduces facilities for working with threads. To enable threading, we need to add the -pthreads flag when compiling with g and clang on the command line. Example:
g -std=c 11 -Wall -pthread source.cpp
With clang it will be:
clang -std=c 11 -Wall -pthread source.cpp
When we compile and link our source code program, an executable file is produced. When we start the executable, the program gets loaded into memory and starts running. This running program is called a process. When we start multiple executable files, we can have multiple processes. Each process has its memory, its own address space. Within a process, there can be multiple threads. What are the threads? Threads or threads of execution are an OS mechanism that allows us to execute multiple pieces of code concurrently/simultaneously.
For example, we can execute multiple functions concurrently using threads. In a broader sense, concurrently can also mean in parallel. A thread is part of the process. A process can spawn one or more threads. Threads share the same memory and thus can communicate with each other using this shared memory.
To create a thread object, we use the std::thread class template from a header file. Once defined, the thread starts executing. To create a thread that executes a code inside a function, we supply the function name to the thread constructor as a parameter. Example:
#include <iostream>#include <thread>void function1(){ for (int i = 0; i < 10; i ) { std::cout << "Executing function1." << 'n'; }}int main(){ std::thread t1{ function1 }; // create and start a thread t1.join(); // wait for the t1 thread to finish}
Here we have defined a thread called t1 that executes a function function1. We supply the function name to the std::thread constructor as a first parameter. In a way, our program now has a main thread, which is the main() function itself, and the t1 thread, which was created from the main thread. The .join() member function says: “hey, main thread, please wait for me to finish my work before continuing with yours.” If we left out the .join() function, the main thread would finish executing before the t1 thread has finished its work. We avoid this by joining the child thread to the main thread.
If our function accepts parameters, we can pass those parameters when constructing the std::thread object:
#include <iostream>#include <thread>#include <string>void function1(const std::string& param){ for (int i = 0; i < 10; i ) { std::cout << "Executing function1, " << param << 'n'; }}int main(){ std::thread t1{ function1, "Hello World from a thread." }; t1.join();}
We can spawn multiple threads in our program/process by constructing multiple std::thread objects. An example where we have two threads executing two different functions concurrently/simultaneously:
#include <iostream>#include <thread>void function1(){ for (int i = 0; i < 10; i ) { std::cout << "Executing function1." << 'n'; }}void function2(){ for (int i = 0; i < 10; i ) { std::cout << "Executing function2." << 'n'; }}int main(){ std::thread t1{ function1 }; std::thread t2{ function2 }; t1.join(); t2.join();}
This example creates two threads executing two different functions concurrently.
The function1 code executes in a thread t1, and the function2 code executes in a separate thread called t2.
We can also have multiple threads executing code from the same function concurrently:
#include <iostream>#include <thread>#include <string>void myfunction(const std::string& param){ for (int i = 0; i < 10; i ) { std::cout << "Executing function from a " << param << 'n'; }}int main(){ std::thread t1{ myfunction, "Thread 1" }; std::thread t2{ myfunction, "Thread 2" }; t1.join(); t2.join();}
Threads sometimes need to access the same object. In our example, both threads are accessing the global std::cout object in order to output the data. This can be a problem. Accessing the std::cout object from two different threads at the same time allows one thread to write a little to it, then another thread jumps in and writes a little to it, and we can end up with some strange text in the console window:
Executi.Executingng function1.Executing function2.
This means we need to synchronize the access to a shared std::cout object somehow. While one thread is writing to it, we need to ensure that the thread does not write to it.
We do so by locking and unlocking mutex-es. A mutex is represented by std::mutex class template from a header. A mutex is a way to synchronize access to shared objects between multiple threads. A thread owns a mutex once it locks the mutex, then performs access to shared data and unlocks the mutex when access to shared data is no longer needed. This ensures only one thread at the time can have access to a shared object, which is std::cout in our case.
Here is an example where two threads execute the same function and guard access to std::cout object by locking and unlocking mutexes:
#include <iostream>#include <thread>#include <string>#include <mutex>std::mutex m; // will guard std::coutvoid myfunction(const std::string& param){ for (int i = 0; i < 10; i ) { m.lock(); std::cout << "Executing function from a " << param << 'n'; m.unlock(); }}int main(){ std::thread t1{ myfunction, "Thread 1" }; std::thread t2{ myfunctiosn, "Thread 2" }; t1.join(); t2.join();}
We can forget to unlock the mutex manually. A better approach is to use the std::lock_guard function instead. It locks the mutex, and once it goes out of scope, it automatically unlocks the mutex. Example:
#include <iostream>#include <thread>#include <string>#include <mutex>std::mutex m; // will guard std::coutvoid myfunction(const std::string& param){ for (int i = 0; i < 10; i ) { std::lock_guard<std::mutex> lg(m); std::cout << "Executing function from a " << param << 'n'; } // lock_guard goes out of scope here and unlocks the mutex}int main(){ std::thread t1{ myfunction, "Thread 1" }; std::thread t2{ myfunction, "Thread 2" }; t1.join(); t2.join();}
11.14 Deleted and Defaulted Functions
If we do not supply a default constructor, the compiler will generate one for us so that we can write:
class MyClass{};int main(){ MyClass o; // OK, there is an implicitly defined default constructor}
However, in certain situations, the default constructor will not be implicitly generated. For example, when we define a copy constructor for our class, the default constructor is implicitly deleted. Example:
#include <iostream>class MyClass{public: MyClass(const MyClass& other) { std::cout << "Copy constructor invoked."; }};int main(){ MyClass o; // Error, there is no default constructor}
To force the instantiation of a default, compiler-generated constructor, we provide the =default specifier in its declaration. Example:
#include <iostream>class MyClass{public: MyClass() = default; // defaulted member function MyClass(const MyClass& other) { std::cout << "Copy constructor invoked."; }};int main(){ MyClass o; // Now OK, the defaulted default constructor is there MyClass o2 = o; // Invoking the copy constructor}
The =default specifier, when used on a member function, means: whatever the language rules, I want this default member function to be there. I do not want it to be implicitly disabled.
Similarly, if we want to disable a member function from appearing, we use the =delete specifier. To disable the copy constructor and copy assignment, we would write:
#include <iostream>class MyClass{public: MyClass() { std::cout << "Default constructor invoked."; } MyClass(const MyClass& other) = delete; // delete the copy constructor MyClass& operator=(const MyClass& other) = delete; // delete the copy // assignment operator};int main(){ MyClass o; // OK MyClass o2 = o; // Error, a call to deleted copy constructor MyClass o3; o3 = o; // Error, a call to deleted copy assignment operator}
These specifiers are mostly used in situations where we want to:
a. force or the instantiation of implicitly defined member functions such as constructors and assignment operators, when we use the =default; expression
b. disable the instantiation of implicitly defined member functions using the =delete; expression
These expressions can also be used on other functions as well.
115 Type Aliases
A type alias is a user-provided name for the existing type. If we want to use a different name for the existing type, we write: using my_type_name = existing_type_name; Example:
#include <iostream>#include <string>#include <vector>using MyInt = int;using MyString = std::string;using MyVector = std::vector<int>;int main(){ MyInt x = 123; MyString s = "Hello World"; MyVector v = { 1, 2, 3, 4, 5 };}
2 C 14
C 14 is an ISO C standard published in 2014. It brings some additions to the language and the standard library, but mainly complements and fixes the C 11 standard. When we say we want to use the C 11 standard, what we actually want is the C 14 standard. Below are some of the new features for C 14.
To compile for C 14, add the -std=c 14 flag to a command-line compilation string if using g or clang compiler. In Visual Studio, choose Project / Options / Configuration Properties / C/C / Language / C Language Standard and choose C 14.
14.1 Binary Literals
Values are represented by literals. So far, we have mentioned three different kinds of binary literals: decimal, hexadecimal, and octal as in the example below:
int main(){ int x = 10; int y = 0xA; int z = 012;}
These three variables have the same value of 10, represented by different number literals. C 14 standard introduces the fourth kind of integral literals called binary literals. Using binary literals, we can represent the value in its binary form. The literal has a 0b prefix, followed by a sequence of ones and zeros representing a value. To represent the number 10 as a binary literal, we write:
int main(){ int x = 0b101010;}
The famous number 42 in binary form would be:
int main(){ int x = 0b1010;}
Important to rememberValues are values; they are some sequence of bits and bytes in memory. What can be different is the value representation. There are decimal, hexadecimal, octal, and binary representations of the value. These different forms of the same thing can be relevant to us humans. To a machine, it is all bits and bytes, transistors, and electrical current.
14.2 Digits Separators
In C 14, we can separate digits with a single quote to make it more readable:
int main(){ int x =100'000'000;}
The compiler ignores the quotes. The separators are only here for our benefit, for example, to split a large number into more readable sections.
14.3 Auto for Functions
We can deduce the function type based on the return statement value:
auto myintfn() // integer{ return 123;}auto mydoublefn() // double{ return 3.14;}int main(){ auto x = myintfn(); // int auto d = mydoublefn(); // double}
14.4 Generic Lambdas
We can use auto parameters in lambda functions now. The type of the parameter will be deduced from the value supplied to a lambda function. This is also called a generic lambda :
#include <iostream>int main(){ auto mylambda = [](auto p) {std::cout << "Lambda parameter: " << p << 'n'; }; mylambda(123); mylambda(3.14);}
14.5 std::make_unique
C 14 introduces a std::make_unique function for creating unique pointers. It is declared inside a header. Prefer this function to raw new operator when creating unique pointers:
#include <iostream>#include <memory>class MyClass{private: int x; double d;public: MyClass(int xx, double dd) : x{ xx }, d{ dd } {} void printdata() { std::cout << "x: " << x << ", d: " << d; }};int main(){ auto p = std::make_unique<MyClass>(123, 456.789); p->printdata();}
3 C 17
The C 17 standard introduces new language and library features and changes some of the language rules.
17.1 Nested Namespaces
Remember how we said we could have nested namespaces ? We can put a namespace into another namespace. We used the following the nest namespaces:
namespace MyNameSpace1{ namespace MyNameSpace2 { namespace MyNameSpace3 { // some code } }}
The C 17 standard allows us to nest namespaces using the namespace resolution operator. The above example can now be rewritten as:
namespace MyNameSpace1::MyNameSpace2::MyNameSpace3{ // some code}
17.2 Constexpr Lambdas
Lambdas can now be a constant expression, meaning they can be evaluated during compile-time:
int main(){ constexpr auto mylambda = [](int x, int y) { return x y; }; static_assert(mylambda(10, 20) == 30, "The lambda condition is not true.");}
An equivalent example where we put the constexpr specifier in the lambda itself, would be:
int main(){ auto mylambda = [](int x, int y) constexpr { return x y; }; static_assert(mylambda(10, 20) == 30, "The lambda condition is not true.");}
This was not the case in earlier C standards.
17.3 Structured Bindings
Structured binding binds the variable names to elements of compile-time known expressions, such as arrays or maps. If we want to have multiple variables taking values of expression elements, we use the structured bindings. The syntax is:
auto [myvar1, myvar2, myvar3] = some_expression;
A simple example where we bound three variables to be aliases for three array elements would be:
int main(){ int arr[] = { 1, 2, 3 }; auto [myvar1, myvar2, myvar3] = arr;}
Now we have defined three integer variables. These variables have array elements values of 1, 2, 3, respectively. These variables are copies of array elements. Making changes to variables does not affect the array elements themselves:
#include <iostream>int main(){ int arr[] = { 1, 2, 3 }; auto [myvar1, myvar2, myvar3] = arr; myvar1 = 10; myvar2 = 20; myvar3 = 30; for (auto el : arr) { std::cout << el << ' '; }}
We can make structured bindings of reference type by using the auto& syntax. This means the variables are now references to array elements and making changes to variables also changes the array elements:
#include <iostream>int main(){ int arr[] = { 1, 2, 3 }; auto& [myvar1, myvar2, myvar3] = arr; myvar1 = 10; myvar2 = 20; myvar3 = 30; for (auto el : arr) { std::cout << el << ' '; }}
It is an excellent way of introducing and binding multiple variables to some container-like expression elements.
17.4 std::filesystem
The std::filesystem library allows us to work with files, paths, and folders on our system. The library is declared through a header. Paths can represent paths to files and paths to folders. To check if a given folder exists, we use:
#include <iostream>#include <filesystem>int main(){ std::filesystem::path folderpath = "C:MyFolder"; if (std::filesystem::exists(folderpath)) { std::cout << "The path: " << folderpath << " exists."; } else { std::cout << "The path: " << folderpath << " does not exist."; }}
Similarly, we can use the std::filesystem::path object to check if a file exists:
#include <iostream>#include <filesystem>int main(){ std::filesystem::path folderpath = "C:MyFoldermyfile.txt"; if (std::filesystem::exists(folderpath)) { std::cout << "The file: " << folderpath << " exists."; } else { std::cout << "The file: " << folderpath << " does not exist."; }}
To iterate over folder elements, we use the std::filesystem::directory_iterator iterator:
#include <iostream>#include <filesystem>int main(){ auto myfolder = "C:MyFolder"; for (auto el : std::filesystem::directory_iterator(myfolder)) { std::cout << el.path() << 'n'; }}
Here we iterate over the directory entries and print each of the elements full path using the .path() member function.
For Linux, we need to adjust the path and use the following instead:
#include <iostream>#include <filesystem>int main(){ auto myfolder = "MyFolder/"; for (auto el : std::filesystem::recursive_directory_iterator(myfolder)) { std::cout << el.path() << 'n'; }}
To iterate over folder elements recursively, we use the std::filesystem::recursive_directory_iterator. This allows us to iterate recursively over all subfolders in a folder. On Windows, we would use:
#include <iostream>#include <filesystem>int main(){ auto myfolder = "C:MyFolder"; for (auto el : std::filesystem::recursive_directory_iterator(myfolder)) { std::cout << el.path() << 'n'; }}
On Linux and similar OS-es, we would use the following path:
#include <iostream>#include <filesystem>int main(){ auto myfolder = "MyFolder/"; for (auto el : std::filesystem::directory_iterator(myfolder)) { std::cout << el.path() << 'n'; }}
Below are some useful utility functions inside the std::filesystem namespace:
std::filesystem::create_directory for creating a directory
std::filesystem::copy for copying files and directories
std::filesystem::remove for removing a file or an empty folder
std::filesystem::remove_all for removing folders and subfolders
17.5 std::string_view
引用一段字符串并保留長度信息。
Copying data can be an expensive operation in terms of CPU usage. Passing substrings as function parameters would require making a copy of substrings. This is a costly operation. The string_view class template is an attempt to rectify that.
The string_view is a non-owning view of a string or a substring. It is a reference to something that is already there in the memory. It is implemented as a pointer to some character sequence plus the size of that sequence. With this kind of structure, we can parse strings efficiently.
The std::string_view is declared inside the header file. To create a string_view from an existing string, we write:
#include <iostream>#include <string>#include <string_view>int main(){ std::string s = "Hello World."; std::string_view sw(s); std::cout << sw;}
To create a string_view for a substring of the first five characters, we use the different constructor overload. This string_view constructor takes a pointer to the first string element and the length of the substring:
#include <iostream>#include <string>#include <string_view>int main(){ std::string s = "Hello World."; std::string_view sw(s.c_str() , 5); std::cout << sw;}
Once we create a string_view, we can use its member functions. To create a substring out of a string_view, we use the .substr() member function. To create a substring, we supply the starting position index and length. To create a substring of the first five characters, we use:
#include <iostream>#include <string>#include <string_view>int main(){ std::string s = "Hello World"; std::string_view sw(s); std::cout << sw.substr(0, 5);}
A string_view allows us to parse (not change) the data that is already in the memory, without having to make copies of the data. This data is owned by another string or character array object.
17.6 std::any
The std::any container can hold a single value of any type. This container is declared inside the header file. Example:
可以理解為一種特殊的void*(void*在解引用前也需要類型顯式轉換為具體特定的類型)。
#include <any>int main(){ std::any a = 345.678; std::any b = true; std::any c = 123;}
To access the value of an std::any object in a safe manner, we cast it to a type of our choice using the std::any_cast function:
#include <iostream>#include <any>int main(){ std::any a = 123; std::cout << "Any accessed as an integer: " << std::any_cast<int>(a) << 'n'; a = 456.789; std::cout << "Any accessed as a double: " << std::any_cast<double>(a) << 'n'; a = true; std::cout << "Any accessed as a boolean: " << std::any_cast<bool>(a) << 'n';}
Important, the std::any_cast will throw an exception if we try to convert, for example, 123 to type double. This function performs only the type-safe conversions.Another std::any member function is .has_value() which checks if the std::any object holds a value:
#include <iostream>#include <any>int main(){ std::any a = 123; if (a.has_value()) { std::cout << "Object a contains a value." << 'n'; } std::any b{}; if (b.has_value()) { std::cout << "Object b contains a value." << 'n'; } else { std::cout << "Object b does not contain a value." << 'n'; }}
17.7 std::variant
There is another type of data in C called union. A union is a type whose data members of different types occupy the same memory. Only one data member can be accessed at a time. The size of a union in memory is the size of its largest data member. The data members overlap in a sense. To define a union type in C , we write:
union MyUnion{ char c; // one byte int x; // four bytes double d; // eight bytes};
Here we declared a union type that can hold characters or integers or doubles. The size of this union is the size of its largest data member double, which is probably eight bytes, depending on the implementation. Although the union declares multiple data members, it can only hold a value of one member at any given time. This is because all the data members share the same memory location. And we can only access the member that was the last written-to. Example:
#include <iostream>union MyUnion{ char c; // one byte int x; // four bytes double d; // eight bytes};int main(){ MyUnion o; o.c = 'A'; std::cout << o.c << 'n'; // accessing o.x or o.d is undefined behavior at this point o.x = 123; std::cout << o.c; // accessing o.c or o.d is undefined behavior at this point o.d = 456.789; std::cout << o.c; // accessing o.c or o.x is undefined behavior at this point}
C 17 introduces a new way of working with unions using the std::variant class template from a header. This class template offers a type-safe way of storing and accessing a union. To declare a variant using a std::variant, we would write:
#include <variant>int main(){ std::variant<char, int, double> myvariant;}
This example defines a variant that can hold three types. When we initialize or assign a value to a variant, an appropriate type is chosen. For example, if we initialize a variant with a character value, the variant will currently hold a char data member. Accessing other members at this point will throw an exception. Example:
#include <iostream>#include <variant>int main(){ std::variant<char, int, double> myvariant{ 'a' }; // variant now holds // a char std::cout << std::get<0>(myvariant) << 'n'; // obtain a data member by // index std::cout << std::get<char>(myvariant) << 'n'; // obtain a data member // by type myvariant = 1024; // variant now holds an int std::cout << std::get<1>(myvariant) << 'n'; // by index std::cout << std::get<int>(myvariant) << 'n'; // by type myvariant = 123.456; // variant now holds a double}
We can access a variant value by index using the std::get(variant_name) function. Or we can access the variant value by a type name using: std::get(variant_name). If we tried to access a wrong type or wrong index member, an exception of type const std::bad_variant_access& would be raised. Example:
#include <iostream>#include <variant>int main(){ std::variant<int, double> myvariant{ 123 }; // variant now holds an int std::cout << "Current variant: " << std::get<int>(myvariant) << 'n'; try { std::cout << std::get<double>(myvariant) << 'n'; // exception is // raised } catch (const std::bad_variant_access& ex) { std::cout << "Exception raised. Description: " << ex.what(); }}
We define a variant that can hold either int or double. We initialize the variant with a 123 literal of type int. So now our variant holds an int data member. We can access that member using the index of 0 or a type name which we supply to the std::get function. Then we try to access the wrong data member of type double. An exception is raised. And the particular type of that exception is std::bad_variant_access. In the catch block, we handle the exception by parsing the parameter we named ex. A parameter is of type std::bad_variant_access, which has a .what() member function that provides a short description of the exception.
20.4 C 20
The C 20 standard promises to bring some big additions to the language. Its impact on the existing standards is said to be as big as the C 11 was to a C 98/C 03 standard. At the time of writing, the C 20 standard is to be ratified around May 2020. The full implementation and the support in the compilers should follow. Some of the following things may, at first glance, seem intimidating, especially when beginning C . However, do not worry. At the time of writing, none of the compilers fully support the C 20 standard, but that is about to change. Once the compilers fully support the C 20 standard, trying out the examples will be much easier. With that in mind, let us go through some of the most exciting C 20 features.
20.1 Modules
Modules are the new C 20 feature, which aims to eliminate the need for the separation of code into header and source files. So far, in traditional C , we have organized our source code using headers files and source files. We keep our declarations/interfaces in header files. We put our definitions/implementations in source files. For example, we have a header file with a function declaration:
mylibrary.h
#ifndef MYLIBRARY_H#define MYLIBRARY_Hint myfunction();#endif // !MYLIBRARY_H39
Here we declare a function called myfunction(). We surround the code with header guards, which ensures the header file is not included multiple times during the compilation. And we have a source file with the function definition. This source file includes our header file:
// mylibrary.cpp#include "mylibrary.h"int myfunction(){ return 123;}
In our main.cpp file we also include the above header file and call the function:
// main.cpp#include "mylibrary.h"int main(){ int x = myfunction();}
We include the same header multiple times. This increases compilation time. Modules are included only once, and we do not have to separate the code into interface and implementation. One way is to have a single module file, for example, mymodule.cpp where we provide the entire implementation and export of this function.
To create a simple module file which implements and exports the above function, we write:
// mymodule.cppexport module mymodule;export int myfunction() { return 123; }
Explanation: the export module mymodule; line says there is a module called mymodule in this file. In the second line, the export specifier on the function means the function will be visible once the module is imported into the main program.
We include the module in our main program by writing the import mymodule; statement.
// main.cppimport mymodule;int main(){ int x = myfunction();}
In our main program, we import the module and call the exported myfunction() function.
A module can also provide an implementation but does need to export it. If we do not want our function to be visible to the main program, we will omit the export specifier in the module. This makes the implementation private to the module:
export module mymodule;export int myfunction() { return 123; }int myprivatefunction() { return 456; }
If we have a module with a namespace in it, and a declaration inside that namespace is exported, the entire namespace is exported. Within that namespace, only the exported functions are visible Example:
// mymodule2.cppexport module mymodule2;namespace MyModule{ export int myfunction() { return 123; }}
main2.cpp:
import mymodule2;int main(){ int x = MyModule::myfunction();}
20.2 Concepts
類型總是定義了一組特定的操作。模板實現的泛型有些不能滿足特定類型的需要,一種方法是類型特化,concepts的方法是顯式聲明類型的特定要求。
Remember the class templates and function templates providing generic types T? If we want our template argument T to satisfy certain requirements, then we use concepts. In other words, we want our T to satisfy certain compile-time criteria. The signature for a concept is:
templateconcept concept_name = requires (T var_name) { reqirement_expression; };
The second line defines a concept name followed by a reserved word requires, followed by an optional template argument T and a local var_name, followed by a requirement_expression which is a constexpr of type bool.
In a nutshell, the concept predicate specifies the requirements a template argument must satisfy in order to be used in a template. Some of the requirements we can write ourselves, some are already pre-made.
We can say that concepts constrain types to certain requirements. They can also be seen as a sort of compile-time assertions for our template types.
For example, if we want a template argument to be incrementable by one, we will specify the concept for it:
template <typename T>concept MustBeIncrementable = requires (T x) { x = 1; };
To use this concept in a template, we write:
template<MustBeIncrementable T>void myfunction(T x){ // code goes in here}
Another way to include the concept into our template is:
template<typename T> requires MustBeIncrementable <T>void myfunction(T x){ // code goes in here}
A full working example would be:
#include <iostream>#include <concepts>template <typename T>concept MustBeIncrementable = requires (T x) { x ; };template<MustBeIncrementable T>void myfunction(T x){ x = 1; std::cout << x << 'n';}int main(){ myfunction<char>(42); // OK myfunction<int>(123); // OK myfunction<double>(345.678); // OK}
This concept ensures our argument x of type T must be able to accept operator , and the argument must be able to be incremented by one. This check is performed during the compile-time. The requirement is indeed true for types char, int, and double. If we used a type for which the requirement is not fulfilled, the compiler would issue a compile-time error.
We can combine multiple concepts. Let us, for example, have a concept that requires the T argument to be an even or an odd number.
template <typename T>concept MustBeEvenOrOdd = requires (T x) { x % 2; };
Now our template can include both the MustBeIncrementable and MustBeEvenOrOdd concepts:
template<typename T> requires MustBeIncrementable<T> && MustBeEvenNumber<T>;void myfunction(T x){ // code goes in here}
The keyword requires is used both for the expression in the concept and when including the concept into our template class/function.
The complete program, which includes both concept requirements, would be:
#include <iostream>#include <concepts>template <typename T>concept MustBeIncrementable = requires (T x) { x ; };template <typename T>concept MustBeEvenOrOdd = requires (T x) { x % 2; };template<typename T> requires MustBeIncrementable<T> && MustBeEvenOrOdd<T>void myfunction(T x){ std::cout << "The value conforms to both conditions: " << x << 'n';}int main(){ myfunction<char>(123); // OK myfunction<int>(124); // OK myfunction<double>(345); // Error, a floating point number is not even // nor odd}
In this example, the template will be instantiated if both concept requirements are evaluated to true during compile time. Only the myfunction(123); and myfunction(124); functions can be instantiated and pass the compilation. The arguments of types char and int are indeed incrementable and can be either even or odd. However, the statement myfunction(345); does not pass a compilation. The reason is that the second requirement MustBeEvenOrOdd is not fulfilled as floating-point numbers are neither odd nor even.
Important! Both concepts say: for every x of type T, the statement inside the code-block { } compiles and nothing more. It just compiles. If it compiles, the requirement for that type is fulfilled.
If we want our type T to have a member function, for example, .empty() and we want the result of that function to be convertible to type bool, we write:
template <typename T>concept HasMemberFunction requires (T x){ { x.empty() } -> std::convertible_to(bool);};
There are multiple predefined concepts in the C 20 standard. They check if the type fulfills certain requirements. These predefined concepts are located inside the header. Some of them are:
a. std::integral – specifies the type should be an integral type
b. std::boolean – specifies the type can be used as a boolean type
c. std::move_constructible – specifies that the object of a particular type can be constructed using the move semantics
d. std::movable – specifies that the object of a certain type T can be moved
e. std::signed_integral – says the type is both integral and is a signed integral
20.3 Lambda Templates
We can now use template syntax in our lambda functions . Example:
auto mylambda = []<typename T>(T param){ // code};
For example, to printout the generic type name, using a templated lambda expression, we would write:
#include <iostream>#include <vector>#include <typeinfo>int main(){ auto mylambda = []<typename T>(T param) { std::cout << typeid(T).name() << 'n'; }; std::vector<int> v = { 1, 2, 3, 4, 5 }; mylambda(v); // integer std::vector<double> v2 = { 3.14, 123.456, 7.13 }; mylambda(v2); // double}
20.4 [likely] and [unlikely] Attributes
If we know that some paths of execution are more likely to be executed than others, we can help the compiler optimize the code by placing attributes. We use the [[likely]] attribute before the statement that is more likely to be executed. We can also put the [[unlikely]] attribute before the statement that is unlikely to be executed. For example, the attributes can be used on case branches inside the switch statement:
#include <iostream>void mychoice(int i){ switch (i) { [[likely]] case 1: std::cout << "Likely to be executed."; break; [[unlikely]] case 2: std::cout << "Unlikely to be executed."; break; default: break; }}int main(){ mychoice(1);}
If we want to use these attributes on the if-else branches, we write:
#include <iostream>int main(){ bool choice = true; if (choice) [[likely]] { std::cout << "This statement is likely to be executed."; } else [[unlikely]] { std::cout << "This statement is unlikely to be executed."; }}
20.5 Ranges
A range, in general, is an object that refers to a range of elements. The new C 20 ranges feature is declared inside a header. The ranges themselves are accessed via the std::ranges name. With classic containers such as an std::vector, if we want to sort the data, we would use:
#include <iostream>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 }; std::sort(v.begin(), v.end()); for (auto el : v) { std::cout << el << 'n'; }}
The std::sort function accepts vector’s .begin() and end() iterators. With ranges, it is much simpler, we just provide the name of the range, without iterators:
#include <iostream>#include <ranges>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 3, 5, 2, 1, 4 }; std::ranges::sort(v); for (auto el : v) { std::cout << el << 'n'; }}
Ranges have a feature called adaptors. One of the range adaptors is views. The views adaptors are accessed via std::ranges::views. Views are not owning. They cannot change the values of the underlying elements. It is also said they are lazily executed. This means the code from the views adaptors will not be executed until we iterate over the result of such views.
Let us create an example which uses range views to filter-out even numbers and print only the odd numbers from a vector by creating a range view:
#include <iostream>#include <ranges>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 }; auto oddnumbersview = v | std::views::filter([](int x) { return x % 2 == 1; }); for (auto el : oddnumbersview) { std::cout << el << 'n'; }}
Explanation: we have a simple vector with some elements. Then we create a view range adaptor on that vector, which filters the numbers in the range. For this, we use the pipe operator |. Only the numbers for which the predicate is true are included. In our case, this means the even numbers are excluded. Then we iterate over the filtered view and print out the elements.
Important to note, the underlying vector’s elements are unaffected as we are operating on a view, not on a vector.
Let us create an example which creates a view that returns only numbers greater than 2:
#include <iostream>#include <ranges>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 }; auto greaterthan2view = v | std::views::filter([](int x) { return x > 2; }); for (auto el : greaterthan2view) { std::cout << el << 'n'; }}
Now, let us combine the two views into one big view by separating them with multiple pipe | operators:
#include <iostream>#include <ranges>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 }; auto oddandgreaterthan2 = v | std::views::filter([](int x) { return x % 2 == 1; }) | std::views::filter([](int x) { return x > 2; }); for (auto el : oddandgreaterthan2) { std::cout << el << 'n'; }}
This example creates a view range adaptor containing odd numbers greater than two. We create this view by combining two different range views into one.
Another ranges adaptors are algorithms. The idea is to have the algorithms overload for ranges. To call an algorithm adaptor we use: std::ranges::algorithm_name(parameters). Example using the std::ranges::reverse() algorithm:
#include <iostream>#include <ranges>#include <vector>#include <algorithm>int main(){ std::vector<int> v = { 1, 2, 3, 4, 5 }; std::ranges::reverse(v); for (auto el : v) { std::cout << el << 'n'; }}
Unlike views, the ranges algorithms modify the actual vector content.
40.4.6 Coroutines
A coroutine is a function that can be suspended and be resumed. The ordinary function is a coroutine if it uses any of the following operators in its function body:
a. co_await – suspends the execution of the coroutine until some other computation is performed, that is until the coroutine itself resumes
b. co_yield – suspends a coroutine and return a value to the caller
c. co_return – returns from a coroutine and stops its execution
20.7 std::span
std::string_view定義的是特定的類型(string),std::span是與std:string_view相同概念的全部類型的應用。
Some containers and types store their elements in a sequence, one next to the other. This is the case for arrays and vectors. We can represent such containers with a pointer to their first element plus the length of the container. A std::span class template from a header is just that. A reference to a span of contiguous container elements. One reason to use the std::span, is that it is cheap to construct and copy. Span does not own a vector or an array it references. However, it can change the value of the elements. To create a span from a vector we use:
#include <iostream>#include <vector>#include <span>int main(){ std::vector<int> v = { 1, 2, 3 }; std::span<int> myintspan = v; myintspan[2] = 256; for (auto el : v) { std::cout << el << 'n'; }}
Here, we created a span that references vector elements. Then we used the span to change the vector’s third element. With span, we do not have to worry about passing a pointer and a length around, and we just use the neat syntax of a span wrapper. Since the size of the vector can change, we say our span has a dynamic extent . We can create a fixed-size span from a fixed-sized array. We say our span now has a static extent. Example:
#include <iostream>#include <span>int main(){ int arr[] = { 1, 2, 3, 4, 5 }; std::span<int, 5> myintspan = arr; myintspan[4] = 10; for (auto el : arr) { std::cout << el << 'n'; }}
20.8 Mathematical Constants
C 20 standard introduces a way to represent some of the mathematical constants. To use them, we need to include the header. The constants themselves are inside the std::numbers namespace. The following example shows how to use numbers pi and e, results of logarithmic functions and square roots of numbers 2 and 3:
#include <iostream>#include <numbers>int main(){ std::cout << "Pi: " << std::numbers::pi << 'n'; std::cout << "e: " << std::numbers::e << 'n'; std::cout << "log2(e): " << std::numbers::log2e << 'n'; std::cout << "log10(e): " << std::numbers::log10e << 'n'; std::cout << "ln(2): " << std::numbers::ln2 << 'n'; std::cout << "ln(10): " << std::numbers::ln10 << 'n'; std::cout << "sqrt(2): " << std::numbers::sqrt2 << 'n'; std::cout << "sqrt(3): " << std::numbers::sqrt3 << 'n';}
ref:
Slobodan Dmitrovi? 《Modern C for Absolute Beginners》
-End-