字串變數:
char *str = "Hello";
char word[] = "Hello";
char line[10] = "Hello";
字串常數1:
字串:
字串常數2:
char *s = "Hello, world!";
char s[] = "Hello, world!";
指針還是陣列?
char *str = "Hello";
char word[] = "Hello";
陣列:這個字串在這裏,在一個固定的位置
指針:這個字串不知道在哪裏
如果要構造一個字串 ——> 陣列方式
如果要處理一個字串 ——> 指針方式
char*是字串?
字串輸入輸出:
char string[8];
scanf("%s", string);
printf("%s", string);
scanf讀入一個單詞(到空格、tab或回車爲止)
scanf是不安全的,因爲不知道要讀入的內容的長度
安全的輸入:
char string[8];
scanf("%7s", string);
在%和s之間的數位表示最多允許讀入的字元的數量,這個數位應該比陣列的大小小一
常見錯誤:
char *string;
scanf("%s", string);
誤以爲char*是字串型別,定義了一個字串型別的變數string就可以直接使用了,
空字串:
char buffer[100] = "";
char buffer[] = "";
字串陣列:寫一個數組表達很多個字串
char **a; // 非字串陣列
char a[][10];
char *a[]; // 字串陣列的兩種表達形式:第一種每個字串有固定的長度,用陣列來表示;而第二種沒有固定的長度,因爲每個字串是使用指針來表示的。
程式參數:
int main(int argc, char const *argv[])
argc是argv的個數,argv[0]是命令本身,argv[0]後面的是命令列輸入的參數
#include <stdio.h>
int main(int argc, char const *argv[]) {
int i;
for (i=0; i<argc; i++) {
printf("%d:%s\n", i, argv[i]);
}
return 0;
}
putchar:
int putcahr(int c);
向標準輸出寫一個字元
返回寫了幾個字元,EOF(-1)表示寫失敗
getchar:
int getchar(void);
從標準輸入讀入一個字元
返回型別是int是爲了返回EOF(-1)
#include <stdio.h>
int main(int argc, char const *argv[]) {
int ch;
while ((ch=getcahr()) != EOF) {
putchar(ch);
}
return 0;
}
Crtl+C強制結束
Crtl+Z返回EOF(windows)
string.h:
strlen:
size_t strlen(cosnt char *s);
返回s的字串長度(不包括結尾的0)
#include <stdio.h>
#include <string.h>
size_t mylen(const char *s) {
int idx = 0;
while (s[idx] != '\0') {
idx++;
}
return idx;
}
int main(int argc, char const *argv[]) {
char line[] = "Hello";
printf("strlen=%lu\n", mylen(line));
printf("sizeof=%lu\n", sizeof(line));
return 0;
}
strcmp:
int strcmp(const char *s1, const char *s2);
比較兩個字串,根據ascii碼從左到右依次比較,返回:
#include <stdio.h>
#include <string.h>
int mycmp(const char *s1, const char *s2) {
// int idx = 0;
// while (s1[idx]==s2[idx] && s1[idx]!='\0') {
// idx++;
// }
while (*s1==*s2 && s1!='\0') {
s1++;
s2++;
}
return *s1 - *s2
}
int main(int argc, char const *argv[]) {
char s1[] = "abc";
char s2[] = "abc";
printf("%d\n", mycmp(s1, s2));
printf("%d\n", 'a'-'A');
return 0;
}
strcpy:
char* strcpy(char *restrict dst, const char *restrict src);
把src的字串拷貝到dst
返回dst
複製一個字串:
char *dst = (char*)malloc(strlen(src)+1);
strcpy(dst, src);
#include <stdio.h>
#include <string.h>
char* mycpy(char *dst, const cahr *src) {
// int idx = 0;
// while (src[idx]) {
// dst[idx] = src[idx];
// idx++;
// }
// dst[idx] = '\0';
char *ret = dst;
while (*dst++ == *src++);
*dst = '\0';
return 0;
}
int main(int argc, char const *argv[]) {
char s1[] = "abc";
char s1[] = "abc";
strcpy(s1, s2);
return 0;
}
strcat:
char* strcat(char *restrict s1, const char *restrict s2);
把s2拷貝到s1的後面,接成一個長的字串,會把s1最後的’\0’給覆蓋掉
返回s1
s1必須具有足夠的空間
安全版本:目的地沒有足夠的空間
char* strncpy(char *restrict dst, const char *restrict src, size_t n);
char* strncat(char *restrict s1, const char *restrict s2, size_t n);
int strncpm(const char *s1, const char *s2, size_t n); // 此函數的作用不是爲了安全,是爲了決定比較前n個的字元
字串中找字元:
char* strchr(const char *s, int c); // 返回找到的字元及其後面的字串
char* strrchr(const char *s, int c); // 從右開始找
返回NULL表示沒有找到
如何尋找第二個?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char const *argv[]) {
char s[] = "hello";
char *p = strchr(s, 'l');
char c = *p;
*p = '\0'; // 返回字元l之前的字串
char *t = (char*)malloc(strlen(s)+1);
strcpy(t, s); // 把選定的字元的字串複製到另一個變數
printf("%s\n", t);
free(t);
return 0;
}
字串中找字串:
char* strstr(const char *s1, const char *s2);
char* strcasestr(const char *s1, const char *s2); // 忽略大小寫
常數符號化:
#include <stdio.h>
const int red = 0;
const int yellow =1;
const int green = 2;
int main(int argc, char const *argv[]) {
int color = -1;
char *colorName = NULL;
printf("輸入你喜歡的顏色的代號:");
scanf("%d", &color);
switch (color) {
case red: colorName = "red"; break;
case yellow: colorName = "yellow"; break;
case green: colorName = "green"; break;
default: colorName = "unknown"; break;
}
printf("你喜歡的顏色是%s\n", colorName);
return 0;
}
列舉:
#include <stdio.h>
enum COLOR {RED, YELLOW, GREEN};
int main(int argc, char const *argv[]) {
int color = -1;
char *colorName = NULL;c
printf("輸入你喜歡的顏色的代號:");
scanf("%d", &color);
switch (color) {
case RED: colorName = "red"; break;
case YELLOW: colorName = "yellow"; break;
case GREEN: colorName = "green"; break;
default: colorName = "unknown"; break;
}
printf("你喜歡的顏色是%s\n", colorName);
return 0;
}
列舉:
列舉是一種使用者定義的數據型別,它用關鍵字enum以如下語法來宣告:
enum 列舉型別名字 {名字0, ..., 名字n};
列舉型別名字通常並不真的使用,要用的是在大括號裡的名字,因爲它們就是常數符號,它們的型別是int,值則依次從0到n。如:
enum color {red, yellow, green};
就建立了三個常數,red的值是0,yellow是1,而green是2。
當需要一些可以排列起來的常數值時,定義列舉的意義就是給了這些常數值名字。
#include <stdio.h>
enum color {red, yellow, green};
void f(enum color c);
int main(void) {
enum color t = red;
scanf("%d", &t);
f(t);
return 0;
}
void f(enum color c) {
printf("%d\n", c);
}
套路:自動計數的列舉
#include <stdio.h>
enum COLOR {RED, YELLOW, GREEN, NumCOLORS};
int main(int argc, char const *argv[]) {
int color = -1;
char *ColorNames[NumCOLORS] = {
"red", "yellow", "green",
};
char *colorName = NULL;
printf("輸入你喜歡的顏色的程式碼:");
scanf("%d", &color);
if (color>=0 && color<NumCOLORS) {
colorName = ColorName[color];
} else {
colorName = "unknown";
}
printf("你喜歡的顏色是%s\n", colorName);
return 0;
}
列舉量:
宣告列舉量的時候可以指定值
enum COLOR {RED=1, YELLOW, GREEN=5};
#include <stdio.h>
enum COLOR {RED=1, YELLOW, GREEN=5, NumCOLORS};
int main(int argc, char const *argv[]) {
printf("code of GREEN is %d\n", GREEN);
return 0;
}
列舉只是int:
#include <stdio.h>
enum COLOR {RED=1, YELLOW, GREEN=5, NumCOLORS};
int main(int argc, char const *argv[]) {
enum COLOR color = 0; // 本應該用符號量賦值,如:RED等。但未進行列舉型別轉換,而直接用數位,是因爲編譯器在這方面的放鬆,主要是這個不常用
printf("code of GREEN is %d\n", GREEN);
printf("and color is %d\n", color);
return 0;
}
列舉:
主要作用:定義符號量,而不是當作型別使用。C的列舉不太成功!主要是爲了定義符號量。
宣告結構型別:
#include <stdio.h>
int main(int argc, char const *argv[]) {
struct date {
int month;
int day;
int year;
}; // 注意不要漏了這個分號!
struct date today;
today.month = 07;
today.day = 31;
today.year = 2014;
printf("Today's date is %i-%i-%i.\n", today.year, today.month, today.day);
return 0;
}
在函數內/外?
#include <stdio.h>
struct date {
int month;
int day;
int year;
};
int main(int argc, char const *argv[]) {
struct date today;
today.month = 07;
today.day = 31;
today.year = 2014;
printf("Today's date is %i-%i-%i.\n", today.year, today.month, today.day);
return 0;
}
宣告結構的形式:
struct point {
int x;
int y;
};
struct point p1, p2; // p1和p2都是point裏面有x和y的值
struct {
int x;
int y;
} p1, p2; // p1和p2都是一種無名結構,裏面有x和y
struct point {
int x;
int y;
} p1, p2; // p1和p2都是point裏面有x和y的值
結構的初始化:
#include <stdio.h>
struct date {
int month;
int day;
int year;
};
int main(int argc, char const *argv[]) {
struct date today = {07, 31, 2014};
struct date thismonth = {.month=7, .year=2014}; // 和陣列一樣沒給的值添零
printf("Today's date is %i-%i-%i.\n", today.year, today.month, today.day);
printf("This month is %i-%i-%i.\n", thismonth.year, thismonth.month, thismonth.day);
return 0;
}
結構成員:
結構和陣列有點像
陣列用[]運算子和下標存取其成員
a[0] = 10;
結構用.運算子和名字存取其成員
today.day
student.firstName
p1.x
p1.y
結構運算:
要存取整個結構,直接用結構變數的名字
對於整個結構,可以做賦值、取地址,也可以傳遞給函數參數
p1 = (struct point){5, 10}; // 相當於p1.x = 5; p1.y = 10;
p1 = p2; // 相當於p1.x = p2.x; p1.y = p2.y;
陣列無法做這兩種運算!
結構指針:
和陣列不同,結構變數的名字並不是結構變數的地址,必須使用&運算子
struct date *pDate = &today; // today本身無法列印?類似於int x
結構作爲函數參數:
int numberOfDays(struct date d)
輸入結構:
#include <stdio.h>
struct point {
int x;
int y;
};
void getStruct(struct point);
void output(struct point);
void main() {
struct point y = {0, 0};
getStruct(y);
output(y);
}
void getStruct(struct point p) {
scanf("%d", &p.x);
scanf("%d", &p.y);
printf("%d,%d", p.x, p.y);
}
void output(struct point p) {
printf("%d,%d", p.x, p.y);
}
解決的方案:
struct point inputPoint() {
struct point temp;
scanf("%d", &temp.x);
scanf("%d", &temp.y);
return temp;
}
void main() {
struct point y = {0, 0};
y = inputPoint();
output(y);
}
指向結構的指針:
struct date {
int month;
int day;
int year;
} myday;
struct date *p = &myday;
(*p).month = 12;
p->month = 12;
#include <stdio.h>
struct point {
int x;
int y;
};
struct point* getStruct(struct point*);
void output(struct point);
void print(cosnt struct point *p);
int main(int argc, char const *argv[]) {
struct point y = {0, 0};
getStruct(&y);
output(y);
output(*getStruct(&y));
print(getStruct(&y));
getStruct(&y)->x = 0; // 在賦值的左邊
*getStruct(&y) = (struct point){1, 2};
return 0;
}
struct pint* getStruct(struct point *p) {
scanf("%d", &p->x);
scanf("%d", &p->y);
printf("%d, %d", p->x, p->y);
return p; // 再傳回去一個指針是爲了方便再運算
}
void output(struct point p) {
printf("%d, %d", p.x, p.y);
}
void print(const struct point *p) {
printf("%d, %d", p->x, p->y);
}
結構陣列:
struct date dates[100];
struct date dates[] = {
{4, 5, 2005},
{2, 4, 2005}
};
結構中的結構:
struct dateAndTime {
struct date sdate;
struct time stime;
};
巢狀的結構:
struct point {
int x;
int y;
};
struct rectangle {
struct point pt1;
struct point pt2;
};
// 如果有變數 struct rectangle r;
// 就可以有:r.pt1.x、r.pt1.y,r.pt2.x和r.pt2.y
// 如果有變數定義:struct rectangle r, *rp; rp = &r;
// 那麼下面 下麪的四種形式是等價的:
r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x
// 但是沒有rp->pt1->x(因爲pt1不是指針)
結構中的結構的陣列:
#include <stdio.h>
struct point {
int x;
int y;
};
struct rectangle {
struct point p1;
struct point p2;
};
void printRect(struct rectangel r) {
printf("<%d,%d> to <%d,%d>\n", r.p1.x, r.p1.y, r.p2.x, r.p2.y);
}
int main(int argc, char const *argv[]) {
int i;
struct rectangle rects[] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}; // 2 rectangle
for (i=0; i<2; i++) printRect(rects[i]);
return 0;
}
struct的記憶體大小是成員個數n*成員裡佔據最大空間的成員佔據的空間,而地址是連續的,但是中間會有記憶體縫隙
自定義數據型別:(typedef 方法看最後一個單詞是它的名字)
C語言提供了一個叫做 typedef 的功能來宣告一個已有的數據型別的新名字。比如:
typedef int Length // 使得Length成爲int型別的別名
這樣,Length這個名字就可以代替int出現在變數定義和參數宣告的地方了:
Length a, b, len;
Length numbers[10];
Typedef:宣告新的型別的名字
typedef long int64_t; // 過載已有的型別名字,新名字的含義更清晰,具有可移植性
typedef struct ADate {
int month;
int day;
int year;
} Date; // 簡化了複雜的名字
int64_t i = 100000000000;
Date d = {9, 1, 2005};
聯合:相似於struct
union AnElt {
int i;
char c;
} elt1, elt2;
elt1.i = 4;
elt2.c = 'a';
elt2.i = 0xDEADBEEF;
作用:可以得到一個int、double、flaot等內部的各個位元組。
檔案操作,把一個整數以二進制的形式寫到檔案裡的時候,做讀寫的中間媒介。
#include <stdio.h>
typedef union {
int i;
char ch[sizeof(int)];
} CHI;
int main(int argc, char const *argv[]){
CHI chi;
unsigned int i;
chi.i = 1234;
for (i=0; i<sizeof(int); i++) {
printf("%02hhX", chi.ch[i]);
}
printf("\n");
return 0;
}