視頻講解:
http://v.youku.com/v_show/id_XNjE2NzI3Mzgw.html
視頻自己錄的,自認為這個視頻講解的有點含糊,而且廢話多、錯誤多- -,又有很多沒有講到,所以特給出文字講解。
本文主要講解了字符與字符串的內存分配、常量字符串的表示、指針、數據轉換方面的內容.
1 編碼
ASCII 可以存放所有的拉丁字符,一個字符占1個字節
ANSI ANSI碼可以算是ASCII碼的一種擴展,ANSI又包括了GB2312, BIG5, JIS等編碼標准(在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文操作系統下,ANSI 編碼代表 JIS 編碼).
ANSI編碼可以存放拉丁字符和非拉丁字符,GB2312編碼中一個拉丁字符占1個字節,一個漢字相當於兩個拉丁字符占2個字節.
純文本文件包括代碼文件(.c .cpp .java .html等)在內一般都是以ANSI編碼存儲.
Unicode 又稱萬國碼、單一碼,它可以存放世界上所有國家所使用的文字,每一個字符都占用2個字節,不管是拉丁字符還是非拉丁字符.
在C語言要表示一個字符或字符串常量是Unicode編碼,可以使用一個宏:_T("") 定義於:tchar.h,存儲Unicode編碼數據可以使用 wchar_t 寬字符數據類型.wchar_t數據類型一般為16位或32位,不同的C或C++庫有不同的規定.
2 所有字符串均以 '\0’ 結尾
在C語言當中,所以的以雙引號括起來的字符串的末尾都默認加上了一個 \0.
如 "abcde” 實際上是 abcde\0 .
\0 表示了一個字符串的末尾,實際也占用了一個字節.
要注意的是,只有在字符串中才會默認加上 \0,如 'a' 像以單引號括起來的單個字符的後面是不會加 \0 的,而 "a" 這樣用雙引號括起來的不管是一個字符還是幾個字符,都會加上 \0.
3 ‘ 單引號 與 " 雙引號表代表的值
char ch = ‘A’; 其中 用 ' 單引號括起來的,實質上其實是代表的一個范圍為0~255的整型數據,這個整型值對應著其字符在ASCII碼中所對應的整型數據(詳細可見MSDN-ASCII Character Codes).char 也是一個整型數據,和其它整型數據不同的是,char 數據默認是無符號的,占用1字節,數據大小范圍是0~255.列如 char ch = 65; 在這裡我們可以直接將一個整型數據賦值給 char 類型變量 ch.如果我們用printf(“%c\n”, ch); 那麼不管是賦給ch的是 'A' 還是 65(65 在ASCII碼中對應了字符 大寫字母A),輸出結果都是 A.當然我們也用 %d 以整型數據輸出它.
例子:
#include <stdio.h>
main(
a =b =
A, 65
A, 65
char *string = “abcd”; 其中,我們可以將 "abcd" 賦值給一個字符指針的原因是: 以雙引號括起來的字符串,整個表達式的值是一個指針.在這個語句中,字符串 abcd\0 是一個常量.在程序運行的時候會自動判斷內存中是否已經存放了 abcd\0 這個數據,如果存放了雙引號表達式就代表了字符串中第一個元素的地址,沒有存放則創建.所以 "abcd" 這個雙引號表達式代表的指針就指向了字符串中的第一個元素 a.因為字符串 abcd\0 是一個常量,所以我們不能通過 string 去修改它的值,但是我們可以去修改 string 指向的地址,如: string = “efg”; 這裡的 "efg" 和 "abcd" 是同一個概念.
#include <stdio.h>
main(
* =, str,
abcd, 0x40c000
4 char[] 字符數組
char string[5] = “abcd”; 這個語句表示的是將常量 abcd\0 復制到所分配的5字節內存空間中去,與char *string = “abcd”;不同之處就在於,前者是將一個內存區域中的數據復制到另外一個內存區域裡去,後者是定義一個指針讓它指向了這一個內存區域.
深入了解過數組的朋友應該也知道,不管是什麼數組,定義的數組名其實都是一個指針.不過這個指針不同於普通指針的是,這一個指針指向的地址不能被改變,指針string是一個常量.這個指針指向了數組的第一個元素,程序以這一個指針來找到整個數組.在 char string[5] = “abcd”; 中,string 就指向了第一個元素的地址,也就是 abcd\0 中 a 的地址.
即使我們能夠改變 string 的值也是不合理的,因為 string 指向的不是常量區的 abcd\0 ,而是程序自己分配了內存空間存入了的 abcd\0 指針,如果我們修改 string 的值,我們就無法找到這個字符串變量所在的內存地址了.而我們如果不調用外部函數要改變一個char [] 字符串的值的話,就只能通過下標來一個一個修改每一個字符的值從而修改整個字符串,如: string[0] = ‘e’; string[1] = ‘f’; …… 當然如果這樣修改起來將會非常的繁瑣,所以我們可以用到一個C語言中給我們提供的用於 char 字符串復制的函數: strcpy.
strcpy 函數原型:char *strcpy( char *strDestination, const char *strSource );
參數1:strDestination 指向目標字符串的首地址;
參數2:strSource 指向源字符串的首地址.
返回值:返回指向 strDestination 的指針.
對於數據類型 const char * 的講解可見下一章.
這個函數的功能就是將 strSource 中的數據復制到 strDestination 中,其返回值我們一般用不到,可忽略.這個函數的功能等價於 char string[5] = “abcd”; 中的 = 號,而與這整個語句不同的是,strcpy 不會分配新的內存空間.
函數使用示例: 改變 string 的值為 efg:
strcpy(string, “efg");
更多的 char 字符字符串操作函數可以查閱 MSDN-String Manipulation Routines
char 數組的性質和其它數據類型的數組一樣,也可以通過一個指向第一個元素的非常量指針進行遞增從而修改元素的值:
[] =
*pChar =
*pChar = ‘e’;
*(++pChar) = ;
5 const char * 與 char *const
const char * a; 這個語句定義了一個常量指針 a,也就是指向常量的指針,意思是說不能通過 a 修改它指向內存地址中的值.要注意這裡是不能通過 a 修改它指向地址中的值,並不代表 a 只能指向常量而不能指向變量.只是 a 指向了變量之後,不能通過 a 去修改這個變量的值.另外 char const *a; 語句,與其等價.
char *const b; 這個語句定義了一個指針常量 b,也就是一個指針的常量,意思是說 b 是一個常量,b 的值不能被改變,即 b 的指向的地址不能被改變,可以通過 b 去修改它指向內存中的值.這裡同樣要注意, b 可以指向常量也可以指向變量,只是說 b 指向誰是不能被改變的.而如果 b 指向了一個常量,那麼當然通過 b 去修改這個常量的值還是錯誤的.因為這裡定義的是一個常量的變量,所以必須在定義的時候就給賦值.
例子:
*a = ;
str[] = ;
* b = str;
*c = str;
= ;
*a = ;
b = ;
*b = ;
*++b = ;
b[] = ;
*c = ;
如果我們要使用 malloc 函數來為一個數組分配內存空間,並且讓它和直接定義一個數組一樣,指向第一個元素的指針的值不能被改變,那麼我們就可以這樣來定義:
char *const string = (char *)malloc(5);
6 字符、字符串與數值間的轉換
int i = (ch-48); 這個語句完成了單個字符 ch 轉化為數字並存入整型變量 i 的功能.
因為字符 0 在 ASCII 碼中對應了 48,後面的數值也是以 1 遞增,所以用它對應的ASCII碼減去48就是這單個字符的整數形式.
如此,我們也可以反過來,實現將整型轉換成字符: char ch = (i+48);
字符串之間的轉換,我們不能同時將一個字符串中的所有字符進行轉換,不調用外部函數的話,我們只能利用上述這一特性把字符串一個一個的轉換,代碼示例:
#include <stdio.h> StringInt( * val = index = ( strlen(str) - ); pn = ; f = , i = ; *pChar = ( == *pChar) { index--= *pChar++ (index >= = (i=; i<=index; i++*= += ( (*pChar++) - ) *-- ( ==*= -; main(, StringInt(, StringInt(, StringInt(, StringInt( /*
參考輸出結果:
333
0
0
-321
*/
上述代碼中的 StringInt 函數便可以把一個字符串轉換成 整型數據並返回,如果我們每個人都要去自己寫一個用於這一轉換的函數那麼是會浪費很多時間的,在C語言中給我們提供了以下幾個函數,可以十分方便地讓我們完成數值與字符間的轉換
字符串->數值:
int atoi( const char *string ); 將一個字符串轉換以整型返回,功能與上述代碼的 StringInt 完全相同.
double atof( const char *string ); 將一個字符串以雙精度浮點型數據並返回
__int64 _atoi64( const char *string ); 將一個字符串以 64 位整型返回
long atol( const char *string ); 將一個字符串以長整型返回
數值->字符串:
char *_itoa( int value, char *string, int radix ); 將一個整型數據 value 轉換為以 radix 進制表示的數據字符串存入 string 並返回指向 string 的指針.
char *_i64toa( __int64 value, char *string, int radix ); 同上,這裡只是value的數據類型不同,功能相同.
char * _ui64toa( unsigned _int64 value, char *string, int radix ); 同上.
wchar_t * _itow( int value, wchar_t *string, int radix ); 將一個整型數據 value 轉換為以 radix 進制表示的數據字符串存入寬字符串 string 並返回指向 string 的指針.
wchar_t * _i64tow( __int64 value, wchar_t *string, int radix ); 同上.
wchar_t * _ui64tow( unsigned __int64 value, wchar_t *string, int radix ); 同上.
更多的C語言數據轉換函數可查閱MSDN-Data Conversion
格式化數據操作函數sprintf 與 sscanf :
int sprintf( char *buffer, const char *format [, argument] ... ); 格式化存放數據到字符串.第一個參數為緩沖區,第二個是格式控制字符串,第三個是輸出表列,第二第三個參數和printf中的第一第二個參數的用法是一樣的.返回值是存入 buffer 字符串的長度.要注意,sprintf只是夠使用以下轉換字符:
%% 印出百分比符號,不轉換。
%c 整數轉成對應的 ASCII 字元。
%d 整數轉成十進位。
%f 倍精確度數字轉成浮點數。
%o 整數轉成八進位。
%s 整數轉成字符串。
%x 整數轉成小寫十六進位。
%X 整數轉成大寫十六進位。
int swprintf( wchar_t *buffer, const wchar_t *format [, argument] ... ); 和 sprintf 相同,但是 swprintf 可以使用所有的轉換字符
用法示例:
#include <stdio.h> main( str[] = i = /*
參考輸出結果:
abc,3
*/
int sscanf( const char *buffer, const char *format [, argument ] ... ); 將字符串 buffer 以格式化分割存入格式表列數據.第二第三個參數與 sscanf 的第一第二個參數用法一樣.同時 sscanf 也和 sprintf 一樣,只能使用相同的部分轉換字符.
%[aB'] 匹配a、B、'中一員,貪婪性
%[^a] 匹配非a的任意字符,並且停止讀入,貪婪性
int swscanf( const wchar_t *buffer, const wchar_t *format [, argument ] ... ); 和 sscanf 相同,可以使用所有轉換字符
#include <stdio.h>
int main(void)
{
int a, b;
char str1[10], str2[10], str3[10];
sscanf("123, 456", "%d, %d", &a, &b);
printf("a = %d, b = %d\n", a, b);
sscanf("abchhdad 123 oofg", "%[a-z] %[1]", &str1, &str2);
printf("str1 = %s, str2 = %s\n", str1, str2);
return 0;
}/*
參考輸出結果:
a = 123, b = 456
str1 = abchhdad, str2 = 1
*/
有關 sscanf 的詳細用法可參見百度百科:http://baike.baidu.com/view/1364018.htm