總網頁瀏覽量

顯示具有 C語言 標籤的文章。 顯示所有文章
顯示具有 C語言 標籤的文章。 顯示所有文章

2013年6月6日 星期四

刪掉程式碼註解的code

/*
 * erase.cpp
 *
 *  Created on: 2013/6/6
 *      Author: YYP
 */

#include <iostream>
#include <io.h>
#include <stdio.h>
#include<fcntl.h>
#include <stdlib.h>
#include <string.h>
using namespace std;

void remove_comment(char *buf, size_t size) {
char *p, *end, c;
char* sq_start, *dq_start;
//single qote單引號開始
//double qoute雙引號開始
char* lc_start, *bc_start;
//lc_start:(//)狀況用,指向斜線當作標記
//bc_start:(/*)狀況用,指向斜線當作標記
size_t len;

p = buf;
//檔案初始位置
end = p + size;
//檔案結尾=檔案初始+檔案大小

sq_start = NULL; //單引號 single quote
dq_start = NULL; //雙引號 double quote
lc_start = NULL;
bc_start = NULL;

while (p < end) {
c = *p; //p指向的字元assign給c

switch (c) {
case '\'': //單引號
if (dq_start || lc_start || bc_start) {
//忽略字串與注視中的單引號
p++; //不做任何事,指標移動到下一個
continue;
}
if (sq_start == NULL) {
sq_start = p++;
} else {

len = p++ - sq_start;
if (len == 2 && *(sq_start + 1) == '\\') { //忽略字元中的單引號
continue;

}
sq_start = NULL;
}
break;

case '\"': //遇到雙引號
if (sq_start || lc_start || bc_start) {
p++;
continue;
}
if (dq_start == NULL) {

dq_start = p++;//dq_start指向第一個引號(標記起來),
//之後p往後移動
} else {

if (*(p++ - 1) == '\\') {
//p-1若是斜線
//忽略字串中的雙引號
continue;
}
dq_start = NULL;//在這邊p已經指到第2n個雙引號
//在code裡面雙引號表示字串 雙雙城隊
//所以這裡要將dq_start=NULL
}
break;

case '/':  //遇見斜杠
if (sq_start || dq_start || lc_start || bc_start) {
//會進入這裡表示目前的斜線是被包在字元字串注釋之中
//忽略自串字元或注釋中的斜線
p++;
continue;
}
c = *(p + 1);//斜線的下一個字元,丟給C
if (c == '/') {//如果c是斜線
//有進入這裡就表示雙斜線了

lc_start = p;
//標記第一個斜線
p += 2;
//指標移動兩字元,指向雙斜線之後的字元

} else if (c == '*') {
bc_start = p;
//標記斜線
p += 2;

} else {
//進入這裡就表示該斜線是除號
p++;
}
break;
case '*'://遇到星號
if(sq_start|| dq_start||lc_start||bc_start==NULL)
{ //bc_start不為NULL表示目前p已經指向區塊註解的第一個斜線
//忽略字元、字串或行注釋的星號 還有忽略乘號
p++;
continue;
}

if(*(p+1)!='/')//下一個字元不是斜線"/",也就是說現在的星號不是區塊註解尾端
{
//游標繼續移動,這個星號並非刪除的標的物
//可能是乘號或指標
p++;
continue;
}
p+=2;//p往後移動兩個字元,跳過星號與反斜線(*)
//這樣做是因為要連同將區塊註解符號刪掉

memset(bc_start,' ',p - bc_start);
//將bc_start之後的p-bc_start區塊,全部變成空白自原
bc_start=NULL;
//恢復原狀



break;
case '\n': //遇到分行符號
if(lc_start == NULL){
//lc_start專門指向單行註解,雙斜線之後的第一個字元
//如果遇到單行註解,那麼lc_start不會是NULL
p++;
continue;
}

c=*(p-1);// 反斜線n(\n)的前面是哪個字元

memset(lc_start,' ',(c=='\r'?(p++ -1 ):p++)-lc_start);
//從lc_start開始之後的(c=='\r'?(p++ -1 ):p++)-lc_start字元換成空白字元
//因為windows與linux文字文件換行字元布一樣所以要c=='\r'?(p++ -1 ):p++)

lc_start=NULL;
//清掉註解之後,lc_start就不用標記斜線了

break;

default:
p++;
break;

}

}//while

if(lc_start){
memset(lc_start,' ',p - lc_start);
}


}

int main() {

int fd, n;
char buf[102400];
//裝載102400個字元,讀取檔案太大,會執行不完
fd = open("D:\\uuu.txt", _O_RDONLY, 0);
//open是fcntl.h裡面的東西
//open回傳檔案代號,如果沒有檔案則回傳-1
if (fd == -1) {
cout << "file not open" << endl;
return -1;
}

n = read(fd, buf, sizeof(buf));
//read(檔案,存放點,讀進多少個字元);
//read()回傳讀進多少字元

if (n == -1 || n == 0) {
close(fd);
return -1;
}

remove_comment(buf, n);

*(buf + n) = '\0';
//在buf最末端加上\0以示檔案結束
//不加上此行,printf(buf)會印出,刪掉註解前的部分檔案資訊

printf(buf);
close(fd);

// cout << "file open" << endl;
return 0;

}

2013年5月11日 星期六

C語言回顧

C語言沒有string
但是C++有不過string似乎非C語言的關鍵字

#include<stdio.h>
以#開頭的指令在程式編譯前會由前處理器先行處理。

cout << "ByeByeWorld" << endl;
//相當於printf("ByeByeWorld\n");
將ByeByeWorld輸出到顯示器上,並將指標cursor移到下一行
這種訊息叫做提示prompt

endl 表面上是換行字元
事實上是指示CPU將記憶體中的資料送到輸出資料流中(ex:顯示器 或某檔案)

cin >> variable;
//相當於scanf("%d",&variable);


C語言的if-else跟java有點不一樣
if(name=1)可以編譯過且可以執行


函數:
分三部分函數宣告、函數定義、函數呼叫

void main(){
editdistance(num);

return 0;
}

int editdistance(int n){
int x;
return x;
}


傳直呼叫:editdiatance函數被呼叫時,引數num先複製到記憶體的stack區塊,接著將n放進stack,接收num的值,進行運算完之後return x;將x複製一份到stack之內,最後main再將stack的結果複製回來。


reference參照:
int N =5;
int& M=N;
M是N的參照,alias
有點像java的 String str ="kkk";  String str2=str;
M變數成為N的綽號
更改M會改變N
更改N會改變M
參照宣告時要初始化否則會發出警告

應用:swap function

傳參照使用時機如果不是以下三種狀況最好不要用,避免無意間更改變數值
1.傳遞過去的引數必須改變
2.傳回兩個以上的值
3.有大量數值需要傳遞,如果採用傳直呼叫,將耗費大量時間在數值複製上
例如引數為大量陣列的情況


陣列:
一維陣列:vector
二維陣列:matrix
三維陣列:tensor

陣列宣告事先宣告大小
const int SIZE =5;
double Pressure[SIZE];
陣列名稱Pressure內存陣列起始位置
可以直接function(Pressure);
傳遞參數呼叫function,function使用的陣列跟main使用的陣列是同一個而不是複本
這是傳址呼叫,在function所做的更動作用在原本陣列身上

sizeof(變數);回傳變數所占記憶體大小
其實是變數實際所站的空間與一個char所占空間比值

陣列大小必須在編譯階段就可以確定的整數
不可以宣告成以下這樣
int a=9;
float farr[a];

指標:


double A = 2.5;
double* pF; //宣告pF,變數型態為"指向double變數的指標型態"
pF= &A;//pF儲存A的記憶體位置,指標pF指向變數A
cout<<*pF<<endl;
//印出pF指向的變數內容


注意double*的指標不可以指向int變數
(編譯器不會自動convert)
也就是說double限制住指標可以指向那些型態變數

記住double* pF;
pF資料型態是double*


double x;
x =*pF;
將pF指向的變數內容存到x裡面


*以及&在宣告時候的意義是告知變數型態而不是取址取值運算
double* p;
double &y=x;

陣列指標:
一個指標存放陣列的位置(也等於array[0]的位置)。


const int size =5;
double V[size]={55.66,11.22,33.44,77.88,99.11};
double* pv=V;
//pv存放陣列的位置
int i=0;
for(i=0;i<size;i++){
cout<< *(pv+i)<<endl;
//指標加1等於陣列位移一格
}

V本身就是一個指標,存放陣列的位置
所以for loop也可以寫
*(V+i)也OK啦



二維陣列的指標
利用指標存取二維陣列:
指標的指標

double B[Row][Col]= {{1.8,4.9,6.8},{6.2,4.1,6.3}};
*B表示存取B[0]也就是陣列B的位置
*B+1同等於*(B+1)先加再取,存取B[1]的元素
**B表示存取B[0][0]的位置
**(B+1)存取B[1][0]同等於*(*(B+1))


int i,j;
for(i=0;i<Row;i++){
for(j=0;j<Col;j++)
{

cout << *(*(B+i)+j) << endl;

}

}

指標參數(傳址呼叫)

X=func(&x,&y);
int func(int*  x, int*  y)

回憶:傳參照呼叫
X=func(a,b)
int func(int& x,int& y)
一旦設定指向參考的變數之後,這個參考就無法再指向其他的變數


6.指標未設定初值不可以這樣寫

int *ptr;
*ptr=2;
//會compile error

7.
可以直接將字串存進指標變數
char* ptr="118";
cout<< ptr;//直接印出字串

int x=112;
 int* num=112;//錯誤寫法,只有字串才可以這樣用
cout<< num;

8.
C++的NULL要大寫,NULL是常數定義為 ((void*)0)


9.不論指標指向哪種資料型別記憶體空間
,指標變數大小一律為4bytes  。


10.指標為何要宣告資料型別?
因為程式要透過指標變數所宣告的資料型別,判斷指標所指向的變數是哪種類型。
這樣編譯器可以知道指標的索引增減該移動多少位元
例如:double指標變數指向double 陣列。當變數+1時候邊義器就知道要移動八位元組才會得到下一筆double資料。


所以指向不同型別變數的指標變數不可以互相指派
int x=19;
double* ptr =&x;
這樣ptr往下一筆存資料index會移動八位元組
而int只有四個位元組
ptr存到第三筆資料去了

11.
可以透過強制型別轉換:讓double* 指標變數可以指向int*指標變數

int i=10;
int* ptr_i=&i;
double* ptr_d;
ptr_d=(double*)ptr_i;

cout<<"ptr_i ="<< ptr_i <<endl;
cout<<"*ptr_i="<<*ptr_i<<endl;

cout<<"ptr_d ="<< ptr_d <<endl;
cout<<"*ptr_d="<<*ptr_d<<endl;//印出奇怪的浮點數字   因為編譯器用8bytes解析
cout<<"*((int*)ptr_d)"<< *((int*)ptr_d)<<endl;//印出正確的數字  因為編譯器用4bytes 解析


12.
不定型别指標變數宣告void *
當宣告指標變數時,還不確定指標所指向的記憶體空間,將儲存哪種資料型別時候,可以將它宣告為
不定型別的指標void*


13.不定型別指標變數的強制轉換

int i=10;
int* ptr_i=&i, *ptr_i1=NULL;
void* ptr_void;

ptr_void=ptr_i;//不定型別可以指向各類型別指標

ptr_i1=(int*)ptr_void;
//各類型別指標要指向不定型別必須強制轉型

cout<<&i<<endl;
cout<<ptr_void<<endl;
cout<<ptr_i<<endl;
cout<<ptr_i1<<endl;
//以上四個值都一樣,那三個指標都指向i了
cout<< *(int*)ptr_void<<endl;
//務必再次轉換因為電腦不記得剛剛有轉過型別,否則會錯誤




Eclipse部署C語言環境

1.help→install new software→add→貼上CDT官網
安裝CDT(C++ Develope Tool)
包含一堆C語言的套件

2.
安裝編譯器
下載MinGW
安裝c compile,c++ compile,msys,最底下項目也勾
一共四個項目
之後
設定環境變數
C:\MinGW\bin
C:\MinGW\msys\1.0\bin

C:\MinGW
C:\MinGW\msys\1.0


加入path

如此一來可在cmd
下gcc  g++ awk指令
如同在linux一樣


eclipse方面window→properties→C/C++→build→makefile edit→settings
新增C:\MinGW\bin

window→properties→C/C++→New CDT Project Wizard→Makefile project→binary parser
只有PE windows parser 要打勾

最後新增C++ project→選擇MinGW gcc→finish

大功告成 測試。
之前一直沒安裝成功 出現library not found
推測原因:沒有做紅色字體步驟或者新增專案沒有include資料夾的關係。
且要先clean project之後再build project


新增c++ project時候,eclipse會要開發者選擇編譯器,要選編譯器MinGW
否則想破頭也無法解決binary not found喔

專案之中要有Binaries這項目

eclipse安裝C語言環境真麻煩
編譯過程有點像是之前在ubuntu run C一樣


2013年5月1日 星期三

漢字相似字查詢實作

漢字一共1151個部首組成64031個漢字


編輯距離演算法應用於
兩個字串互相轉換會花掉多少成本


64031的漢字拆解成部首
利用字體部首計算字體之間相似度
如果兩個字之間共同部首越多表示這兩個字體越相似


字串之間比較:
技術上的問題,因為char只有1bytes空間
而中文字佔2bytes
用char比較每個部首之間的相似度很容易出錯
不如將兩格char存進int裡面
這樣一個int代表一個中文字

因此兩個字串之間比較事實上是兩組的數字串互相比較彼此有沒有同樣的數字
例如:比較兩組數字串的相似度
A:300 222 318 349
B:118 222 318 349


棘棗


改正:大陣列改用讀取檔案方式
傳遞陣列如果陣列太大要用傳參照方式
將插入排序改成quick sort

以上方式通通對效能增加有限
效能不好主要是因為巢狀迴圈
七萬*七萬筆資料要處理
而且每一筆都要比較到
所以要跑四十九億次才可跑完

解決方法避免不必要的字串比較
只要找出一千的編輯成本小於20的字就停止搜尋

2013年4月27日 星期六

SCJP觀念筆記以及考取心得

考完之後跟網路上傳說的一樣
猛虎出閘跟教戰手冊考題背一背就可以上場了
約莫四十五題來自這兩本後面習題
其中四十題一模一樣
剩下五題只是小地方修改
感覺很像在考大學通識課的期末考



不過,準備OCJP過程感覺自己有在變強這才是最重要的
重點是過程而不是那張紙
過程補足自己忽略掉的小細節觀念
至少這樣拿這張證照不會心虛

在做練習題時候感覺很像中學考試一樣
書上教大觀念,考試一直考小細節
剛開始做題目時候挫折感真的很重




1.物件強制轉型
父類別物件不可assign給子類別變數(compile erro)



父類別物件不可強制轉型成為子類別物件(可以compile,但是會ClassCastException)


Thingy t = new Thingy();
//Thingy是父類別型態存放父類別物件
DeluxeThingy k= (DeluxeThingy)t;
//強制轉型成子類別型態的t裝不下原本父類別的物件所以發生classcastexcetpion



Thingy t = new DeluxeThingy();
//父類別型態存放子類別物件

DeluxeThingy k= (DeluxeThingy)t;
//雖然t放的是子類別物件,因為t的型態是父類別所以要強制轉型



2.
javac -d course java/Hello.java
這句話意思是編譯java/Hello.java檔案
並且將.class放在目錄course底下


3.
有個檔案Vermouth.class
放在D:Java\course\accessmode底下
檔案內容為
package course.accessmode;
public class Vermouth{   }

Java是專案跟目錄


在D任何資料夾打上底下這行指令,皆可以順利執行
java -classpath  /Java  course.accessmode.Vermouth

在D:Java目錄打上底下這行,可以順利執行
java course.accessmode.Vermouth 


注意:不可以進入accessmode目錄(類別檔所在目錄)
執行java Vermouth
會出現NoClassFound,因為accessmode沒有course\accessmode資料夾
所以找不到

4.宣告介面後不可再宣告同名的類別



5.

short a=5;
int b =6;
a=b;//不給過
a=(short)b;//給過


6.在java裡所有基本型態資料的變數,傳遞到method均已傳值呼叫(pass by value)傳值呼叫不影響原來的變數值
(編譯器將變數值拷貝一份,再將這個拷貝的一份傳遞給method引數)

傳遞陣列或物件是以傳參照(pass by reference)方式傳參照是傳遞變數的參考位置,因此在method更改變數內容原先變數內容也會隨之更改
(編譯器將物件或陣列參考位址拷貝一份 傳給method引數)
此時有兩個變數操作到同一個實體


如果有上萬的元素的陣列,採用傳值呼叫傳遞引數
那麼編譯器勢必要複製上萬的元素的值,給引數
會降低效率
如果採用傳參照 只消複製一個參考位址給引數即可

7.多載:將功能相似的method 以相同名稱命名,編譯器會根據引數的個數與型態自動值星相對應的method。一個多載化的method 引數內容都是獨一無二的。因此如果有引數相同的method,就算傳回型態不同,編譯器也會搞不清楚該呼叫誰。compile error


8.
列舉型態本身也是類別一種
每個項目預設為public final static
當建立物件時MyColor color = MyColor.RED;
會呼叫建構子,而且是每個項目都呼叫一次
以下的例子會呼叫帶有參數的建構子
因此只寫下面這樣會error
必須加上帶有參數的建構子
public enum MyColor{ RED(0xff0000),GREEN(0x00ff00),BLUE(0x0000ff); }