嵌入式C語言基礎(二)

指針是分類型的:
指針++根據類型不同,偏移值也不同。指針和數組,如何讓指針指向數組?
①用數組名 :int array[10],int* p,p=array
②用第一個元素的地址:int array[10],int* p,p=&array[0]
注意:
不要讓指針的偏移位置超出了數組,否則將看到亂碼。

#include <stdio.h>
#include <stdlib.h>
void arrayAdrePrint(int datas[],int size)
{int i;for(i=0;i<size;i++){printf("第%d個元素的地址是:%p\n",i+1,&datas[i]);    }
}
void arrayPrint1(int datas[],int size)
{int i;for(i=0;i<size;i++){printf("arrayPrint1打印第%d個元素的地址是:%p\n",i+1,datas);datas++;    }
}
void arrayPrint2(int* p,int size)
{int i;for(i=0;i<size;i++){printf("arrayPrint2打印第%d個元素的地址是:%p\n",i+1,p);p++;    }
}
int main()
{int *p1;//整型類型只能存放整數的地址char *p2;//字符型只能存放字符數據的地址int a=10;char c='A';p1=&a;p2=&c;//指針++printf("a的地址打印:%p\n",p1);printf("a的地址++打印:%p\n",++p1);printf("c的地址打印:%p\n",p2);printf("c的地址++打印:%p\n",++p2);//結果是:a的地址++后,向后偏移了四個字節,c的地址++后向后偏移了1個字節//原因是:整型占4個字節,字符型占一個字節int len;int array[3]={1,2,3};len=sizeof(array)/sizeof(array[0]);arrayAdrePrint(array,len);//打印數組的地址,由程序結果可知數組元素的地址是連續的。int* parray;parray=array;arrayPrint1(array,len);arrayPrint2(parray,len);//通過指針++的方式訪問數組system("pause");return 0;
}

指針數組函數的綜合:

#include <stdio.h>
#include <stdlib.h>
void initScores(int *datas,int size)
{int i;for(i=0;i<size;i++){printf("請輸入第%d個學生的分數\n",i+1);scanf("%d",datas);if(*datas<0 || *datas>100){printf("輸入錯誤,強制退出\n");system("pause");exit(-1);}datas++;}
}
void arrayPrint(int *datas,int size)
{int i;for(i=0;i<size;i++){printf("第%d個學生的成績是%d\n",i+1,*datas++);}
}
int findMax(int *datas,int size)
{int i;int max=0;for(i=0;i<size;i++){if(*datas>max){max=*datas;        }datas++;}return max;
}
int findMin(int *datas,int size)
{int i;int min=*datas;for(i=0;i<size;i++){if(*datas<=min){min=*datas;        }*datas++;}return min;
}
float findAverge(int *datas,int size)
{int i;int sum=0;float ave;for(i=0;i<size;i++){sum=*datas+sum;datas++;}ave=(float)sum/size;return ave;
}
void printRet(int data1,int data2,float data3)
{printf("最高分是:%d,最低分是:%d,平均分是:%f\n",data1,data2,data3);
}
int main()
{int scores[3];int len;int max;int min;float averge;len=sizeof(scores)/sizeof(scores[0]);//1、初始化數組initScores(scores,len);//函數傳參的過程實際上就是賦值的過程//1.1打印數組arrayPrint(scores,len);//2、找到最高分max=findMax(scores,len);//3、找到最低分min=findMin(scores,len);//4、算出平均分averge=findAverge(scores,len);//5、輸出結果printRet(max,min,averge);system("pause");return 0;
}

為什么要用指針:

①可以將數值存放在固定的地址中

#include <stdio.h>
#include <stdlib.h>
//0060FEF8這個是我的電腦a的地址
//能不能讓a也就是10,強制的保存在我要的地址 比如將a放在0060FEF4地址是十六進制的數
int main()
{int a;//a的地址肯定是系統隨機分配a=10;int *p;p=&a;printf("a's address is %p\n",p);//(int *)0060FEF4表示將這個整型數強制轉化為一個整型數的地址int *p2=(int*)0x0060FEF4;//這種寫法在以后arm架構,裸機編程·,arm驅動可能用到*p2=10;printf("在內存的%p位置,存放值是%d\n",p2,*p2);volatile int *p2=(volatile int*)0x0060FEF4;//volatile是類型修飾符/*寄存器比內存更靠近CPU,所以數據在內存中訪問的更快,當前我們寫的都是單線程的程序從main函數進來,我們定義一個變量是存放在內存中的,這時候我們對變量的訪問是將變量拷貝一份到寄存器,下次cpu訪問的時候直接從寄存器訪問,這樣效率更高,若改變變量的值,它會自動的將變量的值更新到寄存器。若是多線程編程,同樣是變量存放在內存中,兩個線程都可以對變量進行修改(除非對內存做了一些限定),若線程二改變變量的值,他不會通知線程一將變量的值更新到寄存器,這樣CPU用的只就不是最新的值,程序的結果就可能會有問題。而volatile的作用是讓CPU直接訪問內存,不通過寄存器,這樣提高了正確性,但是降低了效率*/system("pause");return 0;
}

volatile和編譯器的優化有關:
編譯器的優化:
在本次線程內,當讀取一個變量時,為了提高讀取速度,編譯器進行優化時會把變量讀取到一個寄存器中,以后再讀取變量值時,就直接從寄存器中讀出;當變量在本線程里改變時,會同時把變量的新值copy到該寄存器中,以保持一致。
當變量因別的線程值發生改變,上面寄存器的值不會相應的改變,從而造成應用程序讀取的值和實際的變量值不一致。
當寄存器因別的線程改變了值,原變量的值也不會改變,也會造成應用程序讀取的值和實際的變量值不一樣。

volatile詳解:博客

②可以通過地址改變變量的值

在這里插入代碼片#include <stdio.h>
#include <stdlib.h>
/*void swap(int a,int b)
{int tmp;tmp=a;a=b;b=tmp;
}這個函數不能交換a和b的值*/
//指針變量本身也有地址,指針變量的值是其他變量的地址
void swap(int *a,int *b)
{int tmp;tmp=*a;*a=*b;*b=tmp;
}
int main()
{int a=5;int b=10;swap(&a,&b);printf("a=%d,b=%d\n",a,b);system("pause");return 0;
}

指針數組(好多個指針放在一個數組里):

#include <stdio.h>
#include <stdlib.h>
void int1Print(int** array)//因為實參本身就是一級指針//所以要想讓數組的首地址作為參數就要用二級指針
{int i;for(i=0;i<3;i++){printf("指針偏移方法:第%d個元素是;%d\n",i+1,**array);//array本身是一級指針//加一個*代表是取指針數組中所存放的地址//加二個*代表取第一個元素地址所存放的值*array++;//表示將指針后移指向數組中下一個地址}
}
void int2Print(int** array)//array是一級指針數組的首地址,它的地址是二級指針
{int i;for(i=0;i<3;i++){printf("數組元素++方法:第%d個元素是;%d\n",i+1,*array[i]);}
}
void address1Print(int** array)
{int i;for(i=0;i<3;i++){printf("第%d個元素是:%p\n",i+1,*array);array++;}
}
void address2Print(int** array)
{int i;for(i=0;i<3;i++){printf("第%d個元素是:%p\n",i+1,array[i]);}
}
int main()
{int a=1;int b=2;int c=3;int i;int array[3];//多個整數組成的數組叫做整數數組int* p;//整形指針變量//定義指針數組格式:類型名* 數組名[] int* parray[3];//指針數組就是多個指針組成的數組,數組中元素是指針變量//指針變量是存放地址的變量parray[0]=&a;parray[1]=&b;parray[2]=&c;//三個普通沒有任何關系的整型變量的地址存入數組int1Print(parray);//通過指針打印數組中地址所存放的數據int2Print(parray);address1Print(parray);//輸出指針數組中的地址address2Print(parray);system("pause");return 0;
}

數組指針(數組的指針):

#include <stdio.h>
#include <stdlib.h>
//上節課說的是“指針數組”,是一個數組里有多個指針
//本次課說的是“數組指針”,一個指針
//不管是何種類型的指針變量,都是存放別人地址的變量
void addressPrint(int* data)
{printf("函數內輸出array2++的地址:%p\n",++data);
}
int main()
{int i;int (*array1)[3];//數組指針的定義,整型數組指針!=整型指針,因為不是一個類型的指針//比如:int* p,int (*array)[],在linux環境下不能p=array//這個數組指針指向的是整個數組,若array1++,偏移的是整個數組所占的字節=3*4=12.//定義格式:數組的類型 (*數組的名字)[]int array2[3]={1,2,3};int* p;//此指針并非是數組指針//僅僅是一個普通的整形指針,剛好指向了數組的首元素地址arrayarray1=array2;p=array2;printf("元素名稱打印數組2的地址是:%p\n",array2);printf("首元素地址打印數組2的地址是:%p\n",&array2[0]);printf("用數組指針賦值打印數組2的地址是:%p\n",array1);printf("用整型指針賦值打印數組2的地址是:%p\n",p);//這幾種方式輸出的地址是一樣的0060FEDCprintf("=================區別如下================\n");//printf("array2++的結果是:%p\n",++p);// printf("array2++的結果是:%p\n",++array2);因為array2是數組名是常量,不能用來++輸出地址,可以將它賦給指針變量++輸出如:printf("array2++的結果是:%p\n",++p);//但是如果將數組名作為參數傳入函數,則可以用++來輸出整個數組的偏移值。如函數addressPrint()addressPrint(array2);//輸出結果:0060FEE0  偏移值:0060FEE0-0060FEDC=4,是一個數組內元素的大小printf("array1++的結果是:%p\n",++array1);//輸出結果:0060FEE8   偏移值:0060FEE8-0060FEDC=12,正好是整個數組的大小system("pause");return 0;
}

函數指針:

#include <stdio.h>
#include <stdlib.h>
//函數指針,顧名思義它是存放一個函數的地址
//如何定義?如何通過指針調用該函數?
void printfWelcome()
{printf("歡迎來到啊哈C\n");
}
int add(int a,int b)
{return a+b;
}
int main()
{int i=10;printf("變量名:i=%d\n",i);//通過變量名來訪問一個變量int* p=&i;printf("指針:i=%d\n",*p);//通過指針訪問變量printfWelcome();//通過函數名來調用函數//如何定義函數指針void (*p2)();// 1、如何表示指針:用* 2、如何知道是函數:用()表示 3、函數指針是專用的,格式要求很強:參數類型,個數,返回值,就像數組指針一樣//如何給函數指針賦值p2=printfWelcome;//這里不能寫括號,函數名就像是地址,就像數組名一樣就是地址//如何通過函數指針調用函數p2();//直接通過指針名字調用(*p2)();//取內容再調用,(*指針名字)()int (*padd)(int a,int b);//定義函數指針,形參可以省略padd=add;printf("結果是:%d\n",padd(1,2));printf("結果是:%d\n",(*padd)(1,2));system("pause");return 0;
}

無類型的指針:

這里引入malloc函數
不對指針進行寫操作就不用malloc給他開辟空間
給新定義的一個指針賦值(指針),也不用給新定義的指針malloc開辟空間

malloc的函數原型是:
extern void *malloc(unsigned int num_bytes);
這個函數返回的是無類型的指針。
頭文件:#include <malloc.h> 或 #include <alloc.h> (注意:alloc.h 與 malloc.h 的內容是完全一致的。)功能:分配長度為num_bytes字節的內存塊說明:如果分配成功則返回指向被分配內存的指針,否則返回空指針NULL。當內存不再使用時,應使用free()函數將內存塊釋放。num_bytes表示大小是為無符號整型一、函數聲明(函數原型)void *malloc(int size);
說明:malloc 向系統申請分配指定size個字節的內存空間。
返回類型是 void* 類型,void* 表示未確定類型的指針。
C,C++規定,void* 類型可以強制轉換為任何其它類型的指針。二、malloc與new的不同點
從函數聲明上可以看出。malloc 和 new 至少有兩個不同:new 返回指定類型的指針,并且可以自動計算所需要大小。
比如:
int *p;p = new int; //返回類型為int* 類型(整數型指針),分配大小為 sizeof(int);或:int* parr;parr = new int [100]; //返回類型為 int* 類型(整數型指針),分配大小為 sizeof(int) * 100;而 malloc 則必須由我們計算要字節數,并且在返回后強行轉換為實際類型的指針。int* p;p = (int *) malloc (sizeof(int));int* p; p++偏移4個字節
char* p; p++偏移1個字節
void* p; p++不知道偏移幾個字節/*int返回的是整型,void并不是沒有返回值,
而是他的返回值是:無類型的,void本身也是一個類型(無類型)*/

第一、malloc 函數返回的是 void * 類型,如果你寫成:p = malloc (sizeof(int)); 則程序無法通過編譯,報錯:“不能將 void* 賦值給 int * 類型變量”。所以必須通過 (int ) 來將強制轉換。
第二、函數的實參為 sizeof(int) ,用于指明一個整型數據需要的大小。如果你寫成:int
p = (int ) malloc (1);代碼也能通過編譯,但事實上只分配了1個字節大小的內存空間,當你往里頭存入一個整數,就會有3個字節無家可歸,而直接“住進鄰居家”!造成的結果是后面的內存中原有數據內容全部被清空。malloc 也可以達到 new [] 的效果,申請出一段連續的內存,方法無非是指定你所需要內存大小。比如想分配100個int類型的空間:int p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100個整數的內存空間。另外有一點不能直接看出的區別是,malloc 只管分配內存,并不能對所得的內存進行初始化,所以得到的一片新內存中,其值將是隨機的。除了分配及最后釋放的方法不一樣以外,通過malloc或new得到指針,在其它操作上保持一致。

總結:
malloc()函數其實就在內存中找一片指定大小的空間,然后將這個空間的首地址范圍給一個指針變量,這里的指針變量可以是一個單獨的指針,也可以是一個數組的首地址,這要看malloc()函數中參數size的具體內容。我們這里malloc分配的內存空間在邏輯上連續的,而在物理上可以連續也可以不連續。對于我們程序員來說,我們關注的是邏輯上的連續,因為操作系統會幫我們安排內存分配,所以我們使用起來就可以當做是連續的。

實例演示(內含用戶輸入數組元素個數指針方式):

#include <stdio.h>
#include <stdlib.h>
//指針可以當做數組名來用,作為數組的首地址,嚴格的說不等于數組,但是可以認為它是個數組一樣的使用而不產生任何問題。
//不過既然這樣,那它應該算是個數組吧。所以,一般我們都用“動態數組”這種名字來稱呼這種東西。
//malloc能操作的是程序中的堆空間,而普通的數組則是存放在棧空間里面的。
/* 堆空間是系統內存中的可用區域,和普遍意義上的“堆(Heap)”不同,基本上可以看作是由空閑內存組成的大鏈表。
嗯,操作系統怎么處理這東西不管了,反正你就可以認為堆空間是可用內存里的一片連續區域。malloc函數的作用就是從這一片內存中劃出一塊空間來。
你可以認為是malloc從內存中找到了一片可以安全存放數據的可用空間,這樣你的數據就可以放在這片空間里面。這片空間的大小是你自己指定的。
通過malloc(字節數)這樣簡單的方法。為了找到這片空間,malloc函數會告訴你這片空間開頭的地址,你可以把它賦值給一個變量存放起來。*/
int main()
{//int a[3];/* int* a=(int*)malloc(3*sizeof(int));//通過malloc開辟內存空間,并強制轉換為int類型的指針//a指向開辟連續內存空間的首地址//a僅僅代表開辟的12字節的整型內存空間的首地址,所以可以作為數組名來為內存寫值,如下:int i;for(i=0;i<3;i++){a[i]=i;//i是整型,占用三個字節,malloc開辟的是連續的空間,}for(i=0;i<3;i++){printf("數組打印:%d\n",a[i]);}*/int n,i;printf("請輸入數組的個數:\n");scanf("%d",&n);printf("n=%d\n",n);int* a=(int*)malloc(n*sizeof(int));if(a==NULL){printf("開辟失敗\n");}for(i=0;i<n;i++){printf("請輸入第%d個學生的成績:\n",i+1);scanf("%d",&a[i]);//注意要取地址}for(i=0;i<n;i++){printf("第%d個學生的成績是:%d \n",i+1,*a++);}system("pause");return 0;
}

詳解malloc函數的博客:malloc

什么是內存泄漏?如何避免?
內存泄漏:主要體現是程序剛跑起來很好,跑幾個小時或者幾天或者幾周程序崩潰。

比如:

while(1){sleep(1);//每隔一秒int* p=(int*)malloc(1024);//開辟1M空間,malloc申請的空間,程序不會主動釋放//linux中的話,程序結束后,系統會回收空間,程序不結束,不釋放如何避免:1、注意,循環中有沒有一直申請開辟內存2、及時合理的釋放  free(p) p=NULL;若不寫p=NULL可能會使p變成野指針野指針:是指在定義指針的時候沒有將指針初始化,比如:int* p,p就是野指針,將p=NULL后就不是了}

指針收官之面試題小測:

#include <stdio.h>
#include <stdlib.h>
int main()
{int a;//定義整形變量int* p;//定義p為指向整型數據的指針變量p=NULL;int a[5];//定義數組a,它有5個元素int* p[4];//定義指針數組p,它由4個指向整形數組的指針元素組成int (*p)[4];//p為指向包含4個元素的一維數組的指針變量void* p;//p是一個指針變量,基本類型為void(空類型),不指向具體的對象int add(int,int);//add為返回整型的函數int (*p)(int a);//p為指向數組的指針,該函數返回一個整型int** p;//p是一個指針變量,他指向整型數據的指針變量int* p();//p為一個指針的函數,該指針指向整型數據system("pause");return 0;
}
int add(int a,int b)//返回值是整型的函數
{return a+b;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272290.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272290.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272290.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linus中幫助命令man

使用方法&#xff1a;man 可以查看想要使用命令的詳細使用方法 man 命令

大牛是怎么思考設計SQL優化方案的?

作者&#xff1a;慘綠少年https://www.cnblogs.com/clsn/p/8214048.html在進行MySQL的優化之前&#xff0c;必須要了解的就是MySQL的查詢過程&#xff0c;很多查詢優化工作實際上就是遵循一些原則&#xff0c;讓MySQL的優化器能夠按照預想的合理方式運行而已。圖-MySQL查詢過程…

嵌入式C語言基礎(三)

字符串的定義方式及輸出&#xff1a; #include <stdio.h> #include <stdlib.h> void strPrint(char* data,int size) {int i;for(i0;i<size;i){printf("函數打印數組&#xff1a;%c\n",*data);}putchar(\n); } int main() {int i0;int a[3]{1,2,3};c…

web.config配置數據庫連接

第一種&#xff1a;獲取連接字符串 首先要定義命名空間 system.configuration 1. string connstr string constr ConfigurationManager.AppSettings["connstring"]; web.config文件:加在<appsettings>和</appsettings> 之間 <appsettings> <a…

用 Git 和 Github 提高效率的 10 個技巧!

Git 和 GitHub 都是非常強大的工具。即使你已經使用他們很長時間&#xff0c;你也很有可能不知道每個細節。我整理了 Git 和 GitHub 可能提高日常效率的10個常用技巧。GitHub1、快捷鍵: t 和 w在你的源碼瀏覽頁面&#xff0c;按 t 可以快速進入模糊文件名搜索模式&#xff1a;在…

docker顯示鏡像的摘要信息

顯示鏡像的摘要信息 docker images --digests

嵌入式C語言基礎(四)

為什么要用結構體&#xff1f; 在實際問題中&#xff0c;一組數據往往具有不同的數據類型。例如&#xff0c;在學生登記表中&#xff0c;姓名應為字符型;學號可為整型或字符型&#xff1b;年齡應為整型&#xff1b;性別應為字符型&#xff1b;成績可為整型或實型。顯然不能用一…

命令行分析java線程CPU占用

1.使用top命令找出占用cpu最高的JAVA進程pid號 2. 找出占用cpu最高的線程&#xff1a; top -Hp pid -d 1 -n 1 3. 打印占CPU最高JAVA進程pid的堆棧信息 jstack pid > /tmp/stacktrace.log 4. 把占CPU最高線程號碼換算成16進制到stacktrace.log中尋找相應線程16進制值找到…

docker搜索鏡像

docker search 要下載的 OFFICIAL 為ok的表示是官方鏡像

C#操作HttpClient工具類庫

using System; using System.Collections.Generic; using System.Net.Http; using System.Windows.Forms; using System.Configuration; using System.IO; using Newtonsoft.Json; namespace Dcflow { public class HttpHelper { //獲取Configuration對象 public static string…

docker從倉庫找鏡像

docker search -s 數量 要下載的 數量表示倉庫start數

bzoj 1911: [Apio2010]特別行動隊 2011-12-26

1911: [Apio2010]特別行動隊 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 892 Solved: 359[Submit][Status][Discuss] DescriptionInputOutputSample Input4 -1 10 -20 2 2 3 4 Sample Output9HINT Source _________________________________________ 很簡單的動規方程&a…

嵌入式C語言基礎鏈表

什么是鏈表&#xff1f; 鏈表其實就是一種數據結構&#xff0c;所謂的數據結構就是數據存放的思想。 數組、鏈表優缺點&#xff1a; 增加一個元素或者刪除一個元素都很難&#xff0c;因為地址是連續的&#xff0c;刪除一個元素可能會挪動多個元素&#xff0c;不靈活。但是對于鏈…

docker pull 從倉庫拉取鏡像

docker pull 要拉取的鏡像名 等價于 docker pull 要拉取的鏡像名:lastest 拉取固定的鏡像&#xff1a;docker pull 要拉取的鏡像名:版本號 省略lastest表設計就是拉取的最新的

理解js中的原型鏈,prototype與__proto__的關系

說到prototype&#xff0c;就不得不先說下new的過程。 我們先看看這樣一段代碼&#xff1a; 1<script type"text/javascript">2 var Person function () { };3 var p new Person();4</script>很簡單的一段代碼&#xff0c;我們來看看這個new究竟做了什…

C#抓取網頁HTML內容

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Net; using System.Text; using System.IO; using System.Text.RegularExpressions; namespace Web { /// <summary> /// 公共方法類 /// </summary> p…

項目一感應垃圾桶(Wemos)

硬件材料&#xff1a; Wemos D1、SG90舵機、HC-SR04、杜邦線若干、蜂鳴器3.3V&#xff08;可有可無&#xff09; 軟件材料&#xff1a; arduino IDE編譯器、USB轉串口驅動 Wemos D1&#xff1a; 特性&#xff1a; 基于ESP-8266EX及arduino做的一個融合arduino兼容&#xff0…

docker刪除本地所有鏡像

docker rmi -f ${docker images -qa}

PAT1069. The Black Hole of Numbers

//這是到水題&#xff0c;之前因為四位數的原因一直不能A&#xff0c;看了別人的程序&#xff0c;才明白&#xff0c;不夠四位的時候沒考慮到&#xff0c;坑啊。。。。。臉打腫 #include<cstdio>#include<algorithm>using namespace std;int main(){ //freopen(&qu…