------------------------------------------------------------------------------------
回顧:[C語言]指針與字符串
指針的使用:
/**
* main.c by weiChen in 2015-5-4
*/
#include <stdio.h>
//定義函數體,使在main方法中能調用
void swap(int *pa, int *pb);
void minmax(int c[], int len, int *min, int *max);
int divide(int d, int e, int *result);
int main(void)
{
/*
指針應用場景:
1. 交換兩個變量的值
2. 函數返回多個值,某些值就只能通過指針返回;
傳入的參數實際上是需要保存帶回的結果的變量(注:函數中指針的值將作為參數傳入函數);
3. 函數返回運算的狀態,結果通過指針返回;
常用的套路是讓函數返回特殊的不屬於有效范圍內的值來表示出錯;
-1或0(在文件操作會看到大量的例子)
但是當任何數值都是有效的可能結果時,就得分開返回了;
後續的語言(c++,java)采用了異常機制來解決這個問題
4. 指針常見錯誤:定義了指針變量,還沒有指向任何變量,就開始使用指針
*/
int a = 5;
int b = 6;
swap(&a, &b);
printf("a=%d,b=%d\n", a, b);
int c[] = {1,2,3,4,5,9,30,13,45,59};
int min,max;
minmax(c, sizeof(c)/sizeof(c[0]), &min, &max);
printf("min=%d, max=%d\n", min, max);
int d = 19;
int e = 3;
int f;
if(divide(d, e, &f)) {
printf("%d/%d=%d\n", d, e, f);
}
int i = 8;
int *p;
int k;
k = 12;
*p = 12;//未初始化變量,直接給指針賦值;一旦指針所指的位置不可寫,程序將報錯
printf("&i=%p\n", &i);//Segmentation fault
return 0;
}
//交換兩個值
void swap(int *pa, int *pb)
{
int t = *pa;
*pa = *pb;
*pb = t;
}
//求最小最大值
//雖然*min,*max是作為參數傳進去的,但作用是得到結果,帶入
void minmax(int a[], int len, int *min, int *max)
{
int i;
*min = *max = a[0];
for(i=0; i<len; i++) {
if(a[i] < *min) {
*min = a[i];
}
if(a[i] > *max) {
*max = a[i];
}
}
}
//@return 如果除法成功,返回1;否則返回0
int divide(int d, int e, int *result)
{
int ret = 1;
if(e == 0) {
ret = 0;
} else {
*result = d/e;
}
return ret;
}
/**
* main.c by weiChen in 2015-5-4
*/
#include <stdio.sh>
int main(void)
{
/*
指針與const
指針可以是const: 0xaffefado
值可以是const: 45
*/
/*
指針是const: 表示一旦得到了某個變量的地址,不能再指向其他變量
int *const q = &i; //q的值是const,值是i的指針
*q = 26; //OK
q++; //ERROR
*/
/*
所指是const:表示不能通過這個指針去修改那個變量(並不能使得那個變量成為const)
const int *p = &i;
*p = 26; //ERROR, (*p是const)
i = 26; //OK
p = &j; //OK
*/
/*
int i;
const int *p1 = &i; \
| 指針不能被修改
int const *p2 = &i; /
int *const p3 = &i;
//判斷哪個被const了的標志是const在*的前面還是後面
*/
/*
轉換:總是可以把一個非const的值轉換成const的
void f(const int *x); //意思:傳入一個const指針參數,但函數內部不會更改這個指針所指的值
int a = 15; //可以傳入一個非const的指針的值
f(&a); //OK
const int b = a; //也可以傳入一個非const的指針的值
f(&b); //OK
b = a + 1; //ERROR
//當要傳遞的參數的類型比地址大的時候,這是常用的手段:既能用比較少的字節數傳遞值給參數,又能避免函數對外面的變量的修改
*/
/*
const數組:
const int a[] = {1,2,3,4,5,6,};
數組變量已經是const的指針了,這裡的const表明數組的每個單元都是const int (即a[0]等均為const)
所以必須通過初始化進行賦值
*/
/*
保護數組值:
因為把數組傳入函數時傳遞的是地址,所以那個函數內部可以修改數組的值
為了保護數組不被函數破壞,可以設置參數為const
int sum(const int a[], int length); //不希望函數對參數作修改
*/
return 0;
}
指針的計算:
/**
* main.c
*/
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,}; //char數組
char *p = ac; //*p指向數組的第一個單元
printf("p = %p\n", p); //p = 0x7fff51f1dc15
printf("p + 1 = %p\n", p + 1); //p + 1 = 0x7fff51f1dc16
int ai[] = {0,1,2,3,4,5,6,}; //int數組
int *q = ai; //*p指向數組的第一個單元
printf("q = %p\n", q); //q = 0x7fff50160bf0
printf("q + 1 = %p\n", q + 1); //q + 1 = 0x7fff50160bf4
printf("sizeof(char)=%ld\n", sizeof(char)); //1
printf("sizeof(int)=%ld\n", sizeof(int)); //4
/*
指針的計算:
在指針上加1,是移到下一個單元,給實際的指針值加上sizeof類型的值
兩個指針相減,值是兩個指針的差除以sizeof類型的值
*/
// *p -> ac[0]
// *(p+1) -> ac[1]
// *(p+n) -> ac[n]
// 加1如果不加上sizeof(類型)的值,得到的其實並不是我們想要的
// 給指針加1表示要讓指針指向下一個變量:
// int a[10];
// int *p = a;
// *(p+1) -> a[1];
// 如果指針不是指向一片連續分配的空間,如數組,則這種運算沒有意義
/*
指針計算:
給指針加減一個整數(+, +=, -, -=)
遞增遞減(++/--)
兩個指針相減:值是有幾個sizeof類型的值存在
*/
while(*p!=-1) {
printf("%d\n", *p++);
}
/*
*p++
取出p所指的那個數據,順便把p移到下一個位置去
*的優先級雖然高,但是沒有++高
常用於數組類的連續空間操作
在某些cpu指令上,這可以直接被翻譯成一條匯編指令
*/
/*
指針比較:
<, <=, ==, >, >=, != 都可以對指針做
比較他們在內存中的地址
數組中的單元的地址肯定是線性遞增的
*/
/*
0地址:
當然你的內存中有0地址,但是0地址通常是個不能隨便碰的地址
所以你的指針不應該具有0值
因此可以用0地址來表示特殊的事情
返回的指針是無效的
指針沒有被真正初始化(先初始化為0)
NULL是一個預定義的符號,表示0地址,有些編譯器不識別小些的null
有的編譯器不願意你用0來表示0地址
*/
/*
指針的類型:
無論指向什麼類型,所有的指針的大小都是一樣的,因為都是地址
但是指向不同類型的指針是不能直接互相賦值的
這是為了避免用錯指針
*/
/*
指針的類型轉換:
void* 表示不知道指向什麼東西的指針
計算時與char* 相同(但不相通)
指針也可以轉換類型
int *p = &i; void *q = (void*)p;//通過p看i是一個int,通過q看i是一個void(強制類型轉換後賦給q)
這並沒有改變p所指的變量的類型,而是讓後人用不同的眼光通過p看它所指的變量
不再當你是int,而認為是void
*/
/*
用指針來做什麼:
需要傳入較大的數據時用作參數
傳入數組後對數組做操作
函數返回不止一個結果
需要用函數來修改不止一個變量
動態申請的內存
*/
return 0;
}
動態內存分配:
// main.c
// Created by weichen on 15/6/10.
// Copyright (c) 2015年 weichen. All rights reserved.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
/*
動態內存分配
輸入數據:輸入數據時,先告訴你個數,然後再輸入,要記錄每個數據
C99可以用變量做數組定義的大小,C99之前呢?int *a = (int*)malloc(n*sizeof(int));
#include <stdlib.h>
void* malloc(size_t size);
1. 向malloc申請的空間的大小是以字節為單位的
2. 返回的結果是void*,需要類型轉換為自己需要的類型
3. (int*)malloc(n*sizeof(int));
*/
int number;
int* a;
int i;
printf("輸入數量:");
scanf("%d", &number);
// int a[number]; // C99寫法
a = (int*)malloc(number*sizeof(int)); //malloc返回的void*,所以需要類型轉換一下,現在a就可以當做數組使用
for (i=0; i<number; i++) {
scanf("%d", &a[i]);
}
for (i=number; i>=0; i--) {
printf("%d", a[i]);
}
free(a); // 歸還內存
return 0;
}
/**
* main.c
*/
#include <stdio.h> #include <stdlib.h> int main(int argc, const char * argv[]) { // malloc:如果空間申請失敗則返回0或者NULL void *p = 0; //free可以釋放0或NULL,避免free的地址沒有被分配時出錯,習慣初始化指針為0 int cnt = 0; // 如果p的地址不是0(得到了地址),循環繼續 while( (p = malloc(100*1024*1024)) ) { cnt++; } printf("分配了%d00MB的空間\n", cnt); free(p); /* p = malloc(100*1024*1024); p++; // pointer being freed was not allocated p = &i; // pointer being freed was not allocated free(p); */ /* 把申請得到的空間還給“系統” 申請過的空間,最終都應該要還 只能還申請來的空間的首地址 常見問題: 申請了沒有free,長時間運行內存逐漸下降:忘了或找不到合適的free的時機 free過了再free 地址變過了,直接取free */ return 0; }
字符串操作:
/**
* main.c
*/
#include <stdio.h>
int main(int argc, const char * argv[]) {
/*
單字符輸入輸出: putchar,getchar
int putchar(int c);
向標准輸出寫一個字符
返回寫了幾個字符,EOF (-1)表示寫失敗(end of file)
int getchar(void);
從標准輸入讀入一個字符
返回類型是int是為了返回EOF (-1):Windows->Ctrl-Z
Unix->Ctrl-D
*/
int ch;
while( (ch = getchar()) != EOF ) {
putchar(ch);
}
return 0;
/*
字符串數組:
char **a;
a是一個指針,指向另一個指針,那個指針指向一個字符(串)
char a[][1];
*/
}
字符串函數實現:
// main.c // Created by weichen on 15/6/23. // Copyright (c) 2015年 weichen. All rights reserved. #include <stdio.h> #include <string.h>
//自定義求長度的函數 int mylen(const char *s) { int index = 0; //知道長度用for循環, 不知道用while while (s[index] != '\0') { index++; } return index; } int main(int argc, const char * argv[]) {
/**
* size_t strlen(const char *s);
* 返回s的字符串長度(不包括結尾的0)
*/ char line[] = "hello";
printf("strlen=%d\n", strlen(line)); //strlen=5 printf("strlen=%d\n", mylen(line)); printf("sizeof=%d", sizeof(line)); //sizeof=6 return 0; }
// main.c // Created by weichen on 15/6/23. // Copyright (c) 2015年 weichen. All rights reserved.
#include <stdio.h> #include <string.h> int mycmp(const char *s1, const char *s2) { //當做數組處理:用一個整數當做下標,遍歷字符串 /* int index = 0; while (1) { if(s1[index] != s2[index]) { break; } else if(s1[index] == '\0') { break; } index++; } return s1[index] - s2[index]; */ //直接用指針:指針加加,判斷指針所指的值 while (1) { if(*s1 != *s2) { break; } else if(*s1 == '\0') { break; } s1++; s2++; } return *s1 - *s2; //好看的寫法 /* while (*s1 == *s2 && *s1 != '\0') { s1++; s2++; } return *s1 - *s2; */ } int main(int argc, const char * argv[]) { /** * strcmp * int strcmp(const char *s1, const char *s2); * 比較兩個字符串,返回: * 0 :s1==s2 * 1 :s1 >s2 * -1:s1 <s2 */ char s1[] = "abc"; char s2[] = "abc "; printf("%d\n", strcmp(s1, s2)); //0 printf("%d\n", mycmp(s1, s2)); //判斷兩個字符串是否相等的寫法 if(strcmp(s1, s2) == 0) { printf("s1 = s2"); } else { printf("s1 != s2"); } return 0; }
// main.c // Created by weichen on 15/6/23. // Copyright (c) 2015年 weichen. All rights reserved. #include <stdio.h>
#include <string.h> char* mycmp(char *dst, const char *src) { /* 數組方式 int index = 0; while(src[index] != '\0') { dst[index] = src[index]; index++; } //src所有的字符串復制完後,dst還差一個結尾的0,需要加上去 dst[index] = '\0'; return dst; */ //指針方式 char* ret = dst; while (*src != '\0') { *dst = *src; src++; dst++; } *dst = '\0'; return ret; } int main(int argc, const char * argv[]) { /** * strcpy * char * strcpy(char *restrict dst, const char *restrict src); * 把src的字符串拷貝到dst,restrict表明src和dst不重疊(C99) * 返回dst,為了能鏈起代碼來 */ /* 復制一個字符串的套路 char *dst = (char*)malloc(strlen(src) + 1); //不知道要復制的字符串占多大空間,所以需要動態分配內存,strlen只能求出字符串的大小,不包含結尾的0 strcpy(dst, src); */
char s1[] = "abc";
char *str = (char*)malloc(strlen(s1) + 1);
strcpy(str, s1);
printf("%s", str); return 0; }
// main.c
// Created by weichen on 15/6/26.
// Copyright (c) 2015年 weichen. All rights reserved.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
/*
在字符串中找單個字符
char* strchr(const char *s, int c);
char* strrchr(const char *s, int c);
返回NULL表示沒有找到
*/
char s[] = "hello";
char *p = strchr(s, 'l');
printf("%s\n", p); //llo
//找第二個l
char *q = strchr(p + 1, 'l');
printf("%s\n", q); //lo
//從右邊開始找
char *a = strrchr(s, 'l');
printf("%s\n", a); //lo
//復制找到的字符串
char *r = (char*)malloc(strlen(p) + 1); //1.動態分配內存空間
char *t = strcpy(r, p); //2.復制字符串
printf("%s\n", t); //llo
free(r); //3.釋放動態分配的內存空間
//找l之前字符的技巧
char c = *p;
*p = '\0'; //將*p所指的位置替換為\0; s就變成了he\0lo
char *u = (char*)malloc(strlen(s) + 1);
char *v = strcpy(u, s);
printf("%s\n", v); //he
printf("%s\n", s); //he
free(u);
//最後將*p恢復為l
*p = c;
printf("%s\n", s); //hello
/*
字符串中找字符串
char* strstr(const char *s1, const char *s2);
字符串中找字符串,並忽略大小寫
char* strcasestr(const char *s1, const char *s2);
*/
char x[] = "E";
char y[] = "e";
char *z1 = strstr(s, x); //如果第二個參數x改為字符'E',就會報錯,因為函數接受的參數是一個指針;
char *z2 = strstr(s, y);
char *z3 = strcasestr(s, x);
printf("%s\n", z1); //null
printf("%s\n", z2); //ello
printf("%s\n", z3); //ello
/*
連接兩個字符串
extern char* strcat(char *dest, char *src);
*/
char ab[10] = "good";
char *cd = "bye";
strcat(ab, cd);
printf("%s\n", ab); //goodbye
return 0;
}
//注:PHP中strchr是strstr的別名,因為裡面沒有指針一說,所以第一第二個參數均為字符串。
Link:http://www.cnblogs.com/farwish/p/4477897.html
@黑眼詩人 <www.farwish.com>