沒有返回值的函數:
void sum(int begin, int end) {
int i;
int sum = 0;
for (i=begin; i<=end; i++) {
sum += i;
}
printf("%d到%d的和是%d\n", begin, end, sum);
}
函數的先後 先後關係:
void sum(int begin, int end) {
int i;
int sum = 0;
for (i=begin; i<=end; i++) {
sum += i;
}
printf("%d到%d的和是%d\n", begin, end, sum);
}
int main() {
sum(1, 10);
sum(20, 30);
sum(35, 45);
return 0;
}
函數原型:
double max(double a, double b); // 函數原型
int main() {
int a, b, c;
a = 5;
b = 6;
c = max(10, 12);
printf("%d\n", c);
max(12, 13); // 根據原型判斷
return 0;
}
double max(double a, double b) { // 實際的函數頭
}
型別不匹配?
傳過去的是什麼?
void swap(int a, int b);
int main() {
int a = 5;
int b = 6;
swap(a, b);
printf("a=%d b=%d\n", a, b);
return 0;
}
void swap(int a, int b) {
int t = a;
a = b;
b = t;
}
傳值:
本地變數:
變數的生存期和作用域
本地變數的規則:
沒有參數時:
void f(void);
還是
void f();
關於main:
可以在一個函數裡放另一個函數的宣告,但是不能定義另一個函數。
定義陣列:
<型別> 變數名稱[元素數量];
int grades[100];
double weight[20];
元素數量必須是整數
C99之前:元素數量必須是編譯時刻確定的字面量
陣列:
有效的下標範圍:
int a[0]; // 可以存在,但是無用
統計個數範例:
const int number = 10; // 陣列的大小,c99
int x;
int count[number]; // 定義陣列
int i;
for (i=0; i<number; i++) { // 初始化陣列
count[i] = 0;
}
scanf("%d", &x);
while (x!=-1) {
if (x>=0 && x<=9) {
count[x]++; // 陣列參與運算
}
scanf("%d", &x);
}
for (i=0; i<number; i++) { // 遍歷陣列輸出
printf("%d:%d\n", i, count[i]);
}
整合初始化時的定位:
int a[10] = { // c99 ONLY
[0] = 2, [2] = 3, 6,
};
陣列的大小:
sizeof給出整個陣列所佔據的內容的大小,單位是位元組
sizeof(a)/sizeof(a[0])
sizeof(a[0])給出陣列中單個元素的大小,於是相除就得到了陣列的單元個數
這樣的程式碼,一旦修改陣列中初始的數據,不需要修改遍歷的程式碼
陣列的賦值:
int a[] = {2, 4, 6, 7, 1, 3};
int b[] = a;
for (i=0; i<length; i++) {
b[i] = a[i];
}
/*
找出key在陣列a中的位置
@param key 要尋找的數位
@param a 要尋找的陣列
@param length 陣列a的長度
@return 如果找到,返回其在a中的位置;如果找不到則返回-1
*/
int search(int key, int a[], int length);
int main(void) {
int a[] = {2, 4, 6, 7, 1, 3, 5, 9};
int x;
int loc;
printf("請輸入一個數字:");
scanf("%d", &x);
loc = search(x, a, sizeof(a)/sizeof(a[0]));
if (loc != -1) {
printf("%d在第%d個位置上\n", x, loc);
} else {
printf("%d不存在\n", x);
}
return 0;
}
int search(int key, int a[], int length) {
int ret = -1;
int i;
for (i=0; i<length; i++) {
if (a[i] == key) {
ret = i;
break;
}
}
return ret;
}
判斷是否能被已知的且<x的素數整除:
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes) {
int ret = 1;
int i;
for (i=0; i<numberOfKnownPrimes; i++) {
if (x%knownPrimes[i] == 0) {
ret = 0;
break;
}
}
return ret;
}
int main(void) {
const int number = 100;
int prime[number] = {2};
int count = 1;
int i = 3;
while (count < number) {
if (isPrime(i, prime, count)) {
prime[count++] = 1;
}
i++;
}
for (i=0; i<number; i++) {
printf("%d", prime[i]);
if ((i+1)%5) printf("\t");
else printf("\n");
}
return 0;
}
構造素數表:
#include <stdio.h>
int main() {
const int maxNumber = 25;
int isPrime[maxNumber];
int i;
int x;
for (i=0; i<maxNumberl i++) {
isPrime[i] = 1;
}
for (x=2; x<maxNumber; i++) {
if (isPrime[x]) {
for (i=2; i*x<maxNumber; i++) {
isPrime[i*x] = 0;
}
}
}
for (i=2; i<maxNumber; i++) {
if (isPrime[i]) {
printf("%d\t", i);
}
}
printf("\n");
return 0;
}
二維陣列的初始化:
int a[][5] = {
{0, 1, 2, 3, 4},
{2, 3, 4, 5, 6},
};
只能變數取地址
int i;
int* p = &i;
int* p, q;
int *p, q; // 與上一行所表達一樣,p指針,q是int型別變數
存取那個地址上的變數*
int k = *p;
*p = k+1;
*左值之所以叫左值
a[0] = 2;
*p = 3; // 賦值時,p代表的是地址,\*p代表的是地址所指的值。
指針應用場景一:
void swap(int *pa, int *pb) {
int t = *pa;
*pa = *pb;
*pb = t;
}
指針應用場景二a:
指針應用場景二b:
定義了指針變數,還沒有指向任何變數,就開始使用指針
傳入函數的陣列成了什麼?
sizeof(a) == sizeof(int*)
但是可以用陣列的運算子[]進行運算
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes) {
int ret = 1;
int i;
for (i=0; i<numberOfKnownPrimes; i++) {
if (x%knownPrimes[i] == 0) {
ret = 0;
break;
}
}
return ret;
}
陣列變數是特殊的指針:
陣列變數本身表達地址,所以
int a[10]; int *p=a; // 無需用&取地址
但是陣列的單元表達的是變數,需要用&取地址
a == &a[0]
[]運算子可以對陣列做,也可以對指針做:
*運算子可以對指針做,也可以對陣列做:
*a = 25;
陣列變數是const的指針,所以不能被賦值
指針是const:
int* const q = &i; // q是const
*q = 26; // OK
q++; // ERROR
所指是const:
const int *p = &i;
*p = 26; // ERROR!(\*p)是const
i = 26; // OK
p = &j; // OK
轉換:
void f(const int *x); // 表示接受的值,我不會去更改它,你放心!
int a = 15;
f(&a); // ok
const int b = a; // 無論傳入f的值是不是const都可以
f(&b); // ok
b = a + 1; // Error
1+1=2?
int a[10];
int *p = a;
// *(p+1)——>a[1]
指針計算:
指針相減是指相隔了多少個數
*p++
指針比較:
0地址
指針的型別:
指針的型別轉換:
int *p = &i;
void *q = (void*)p;
- 指針的型別就是指針
指針大小和記憶體的編址方式有關,只是恰好與無符號整形大小相同
他的大小是4位元組(32位元)就是類似0012ff78(16進位制 32位元)
注:如果你的電腦是64位元電腦那麼他的大小就是8位元組!- 指針是用來儲存記憶體地址的
記憶體有按32位元編制 編製和按64位元編制 編製之分- 爲什麼要給指針定義型別呢?
只有爲指針定義型別
才能 纔能知道指針所指向的變數的大小
例如: int*p;和 double*q;
那麼讀取*p時 就要從地址p開始讀取4位元組
讀取*q時就要從地址q開始讀取8位元組
用指針來做什麼:
malloc:
#include <stdlib.h>
void* malloc(size_t size);
向malloc申請的空間的大小是以位元組爲單位的
返回的結果是void*,需要型別轉換爲自己需要的型別
(int*)malloc(n*sizeof(int))
沒空間了?
#include <stdio.h>
#include <stdlib.h>
int main(void) {
void *p;
int cnt = 0;
while (p=malloc(100*1024*1024)) {
cnt++;
}
printf("分配了%d00MB的空間\n", cnt);
return 0;
}
free():
常見問題:
字串變數:
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); // 忽略大小寫