無論是 float 還是 double 類型的變量,都有精度限制。所以一定要避免將浮點變量 用「= =」 或 「=」與數字比較,應該設法轉化成「>=」或「<=」形式。 if="" x="">=-EPSINON) && (x<=EPSINON))其中 EPSINON 是允許的誤差(即精度)。
float a = 0.1;
if (a * 10 >= 1.0 - 0.0000001 && a * 10 <= 1.0 + 0.000001)
{
printf("==");
}
else
{
printf("x");
}
二、小寫字母轉大寫字母1.ASSIC碼的辦法#include
void main() {
char a;
printf(「請輸入一個大寫字母:」);
scanf("%c", &a);
printf("\n它對應的大寫字母是:%c\n", a + 32);
}
2.庫函數#include
#include
int main()
{
char s[] = "gasdgasSFASFAS";
int i;
printf("befor toupper :%s\n", s);
for (i = 0; i < sizeof(s); i++)
{
s[i] = toupper(s[i]);
}
printf("after toupper :%s\n", s);
}
三、C語言字符數組和字符指針梳理1.數組退化成指針#include
int size(char a[10])
{
return sizeof(a);
}
int main(void)
{
char a[] = { 'C','h','i','n','a','\0' };
char* p = "China";
char* q = a;
printf("sizeof(a)=%d\n", sizeof(a)); //sizeof(a)=6
printf("sizeof(p)=%d\n", sizeof(p)); //sizeof(p)=4
printf("sizeof(q)=%d\n", sizeof(q)); //sizeof(q)=4
printf("size(a)=%d\n", size(a)); //size(a)=4
return 0;
}
首先C語言裡面不支持數組作為形參來進行調用,例如上述函數int size(char a[])實際上在進行編譯時,是作為指針來處理的,所以上面的函數完全等價於int size(char *a);所以上面printf("size(a)=%d\n", size(a)); 輸出size(a)=4。
數組作為參數傳給函數時,傳的是指針而不是數組,傳遞的是數組的首地址,如 fun (char[8]),fun(char [])都等價於 fun(char*)。
退化為指針;需要數組大小時,需要一個參數傳數組名,另一個傳數組大小:void fun(int* a, size_t n);
2.僅在以下3種情況中,數組不會退化成指針sizeof是一個關鍵字並不是一個函數。
使用sizeof(a)的時候,所以上面sizeof(a)的結果為6位元組。
對數組名取地址操作:&a。&a的類型為pointer to array of 6 chars,而對字符串指針取地址結果為pointer to pointer to char。
使用字符串初始化數組的時候也不會退化。
3.其它情況都會退化成「指向數組首元素的指針」,比如組名做函數參數時,a都會退化成&a[0]指針。如上面的size函數和printf函數中的a[]都是這種情況。
二維數組,char s[10][8]數組的首元素(得理解這個首元素,從一維數組的角度看的)是一維數組s[0],因此退化成&s[0]指針。
4.數組定義字符串和指針定義字符串的區別使用數字定義的字符串只分配字符串所需的空間(上例中為6個字節),數字名沒有單獨的存儲空間;使用指針定義字符串,除了字符串佔用的空間,還要為指針變量分配對應的空間。
數組退化成指針為「常量指針」,不可更改,而直接指向字符串的指針可修改,如:
char a[10] = "China";
const char* p = "China";
a++; // 錯誤的
p++; // 正確的
5.C語言字符串指針(指向字符串的指針)C語言中沒有特定的字符串類型,我們通常是將字符串放在一個字符數組中。如下代碼:
#include
#include
int main() {
char str[] = "http://c.biancheng.net";
int len = strlen(str), i;
//直接輸出字符串
printf("%s\n", str);
//每次輸出一個字符
for (i = 0; i < len; i++) {
printf("%c", str[i]);
}
printf("\n");
return 0;
}
運行結果:
http://c.biancheng.net
http://c.biancheng.net
字符數組當然是數組,那麼我們就可以利用指針對字符數組進行操作。
#include
#include
int main() {
char str[] = "http://c.biancheng.net";
char* pstr = str;
int len = strlen(str), i;
//使用*(pstr+i)
for (i = 0; i < len; i++) {
printf("%c", *(pstr + i));
}
printf("\n");
//使用pstr[i]
for (i = 0; i < len; i++) {
printf("%c", pstr[i]);
}
printf("\n");
//使用*(str+i)
for (i = 0; i < len; i++) {
printf("%c", *(str + i));
}
printf("\n");
return 0;
}
運行結果:
http://c.biancheng.net
http://c.biancheng.net
http://c.biancheng.net
除了字符數組以外,c語言還支持另外一種表示字符的方法,就是直接使用一個指針指向字符串,例如:
char *str = "http://c.biancheng.net";
或者:
char *str;
str = "http://c.biancheng.net";
下面演示如何輸出這種字符串:
#include
#include
int main()
{
char a[] = "fjasdifaisdhfg";
printf("%d\n", &a);
}
#include
#include
int main() {
char* str = "http://c.biancheng.net";
int len = strlen(str), i;
//直接輸出字符串
printf("%s\n", str);
//使用*(str+i)
for (i = 0; i < len; i++) {
printf("%c", *(str + i));
}
printf("\n");
//使用str[i]
for (i = 0; i < len; i++) {
printf("%c", str[i]);
}
printf("\n");
return 0;
}
運行結果:
運行結果:
http://c.biancheng.net
http://c.biancheng.net
http://c.biancheng.net
這樣看起來,字符數組和使用一個指針指向字符串是非常相似的。那麼這兩種表示字符串的區別是什麼?
特別注意:
字符數組和指針字符串的區別是內存中的存儲區域不一樣,字符數組存儲在全局數據區或棧區,指針字符串存儲在常量區。
全局區和棧區的字符串(包括其它數據)有讀取和寫入的權限,而常量區的字符串只有讀取權限,沒有寫入權限。
內存權限不同導致一個明顯的結果就是,字符數組在定義後可以讀取和修改每個字符,而對於指針字符串來說,一旦被定義
後就只能讀取而不能修改,任何對它的賦值都是錯誤的。
#include
int main() {
char* str = "Hello World!";
str = "I love C!"; //正確
str[3] = 'P'; //錯誤
return 0;
}
這段代碼能夠正常編譯和連結,但是在運行時會出現段錯誤(Segment Fault)或者寫入錯誤。
第四行代碼是正確的,可以更改指針變量本身的指向;第5行代碼是錯誤的,不能修改字符串中的字符。
那麼到底使用字符數組還是字符串常量?
在編程過程中,如果只涉及到對字符串的讀取,那麼字符數組和字符串常量都可以滿足要求;如果有寫入(修改)的操作,
那麼只能使用字符數組,不能使用字符串常量。
獲取用戶輸入的字符串就是一個典型的寫入操作,只能使用字符數組,不能使用字符串常量,請看下面的代碼:
#include
int main() {
char str[30];
gets(str);
char* ptr;
gets(ptr);//錯誤
printf("%s\n", str);
return 0;
}
四、Memcpy()和memcpy_s()的用法1、memcpy()函數
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
用法:#include "string.h"
功能:由src所指向內存區域複製count個字節到dest指向的內存區域。
返回值:函數返回指向dest的指針
注意:src和dest所指向的內存區域不能重疊
如果目標內存區域dest已有數據,執行memcp後,將覆蓋count個字節的原有數據。
memcpy的操作對象不局限於某一類數據類型,只要能給出對象的其實地址和內存長度信息,並且對象具有可操作性即可。
2、memcpy_s()函數
原型:errno_t memcpy_s(
void *dest,
size_t numberOfElements,
const void *src,
size_t count
);
參數說明:
dest: 目標地址
numberOfElements: 目標dest的size
src: 源地址
count: 要複製的字節數
注意:要保證numberOfElements >= count。
返回值:成功時返回0,錯誤返回非零值。
五、進程線程間的通信1、消息隊列,是最常用的一種,也是最靈活的一種,通過自定義數據結構,可以傳輸複雜和簡單的數據結構。
在Windows程序設計中,每一個線程都可以擁有自己的消息隊列(UI線程默認自帶消息隊列和消息循環,工作線程需要手動實現消息循環),因此可以採用消息進行線程間通信sendMessage,postMessage。
定義消息#define WM_THREAD_SENDMSG=WM_USER+20;
添加消息函數聲明afx_msg int OnTSendmsg();
添加消息映射ON_MESSAGE(WM_THREAD_SENDMSG,OnTSM)
添加OnTSM()的實現函數;
在線程函數中添加PostMessage消息Post函數
2、使用全局變量
進程中的線程間內存共享,這是比較常用的通信方式和交互方式。
註:定義全局變量時最好使用volatile來定義,以防編譯器對此變量進行優化。
3、使用事件CEvent類實現線程間通信
Event對象有兩種狀態:有信號和無信號,線程可以監視處於有信號狀態的事件,以便在適當的時候執行對事件的操作。
1)創建一個CEvent類的對象:CEvent threadStart;它默認處在未通信狀態;
2)threadStart.SetEvent();使其處於通信狀態;
3)調用WaitForSingleObject()來監視CEvent對象
六、多態#include
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape(int a = 0, int b = 0)
{
width = a;
height = b;
}
virtual int area()
{
cout <<< span=""> "Parent class area :" <<< span=""> endl;
return 0;
}
};
class Rectangle : public Shape {
public:
Rectangle(int a = 0, int b = 0) :Shape(a, b) { }
int area()
{
cout <<< span=""> "Rectangle class area :" <<< span=""> endl;
return (width * height);
}
};
class Triangle : public Shape {
public:
Triangle(int a = 0, int b = 0) :Shape(a, b) { }
int area()
{
cout <<< span=""> "Triangle class area :" <<< span=""> endl;
return (width * height / 2);
}
};
// 程序的主函數
int main()
{
Shape* shape;
Rectangle rec(10, 7);
Triangle tri(10, 5);
// 存儲矩形的地址
shape = &rec;
// 調用矩形的求面積函數 area
shape->area();
// 存儲三角形的地址
shape = &tri;
// 調用三角形的求面積函數 area
shape->area();
return 0;
}