วิธีใช้ GCC กับภาษา C หลายๆไฟล์

วิธี Compile ภาษา C โดยใช้ GCC สำหรับในกรณีที่ไฟล์ .c หลายไฟล์ เป็นโปรเจคเดียวกัน สามารถใช้วิธี compile โดยใช้ GCC ได้ดังนี้

ขั้นแรกให้เรา Compile ไฟล์ .c แต่ละไฟล์ โดยใช้ –c เพื่อบอกว่า compile ไฟล์นี้เท่านั้น ไม่ต้อง link กับไฟล์อื่น

gcc -c ไฟล์ที่1.c
gcc -c ไฟล์ที่2.c

ไปเรื่อยๆ ขึ้นอยู่กับตามจำนวนไฟล์ .c

หลังจากนั้นเราก็จะได้ ไฟล์ object นามสกุล .o ออกมา เสร็จแล้วทำการ link ไฟล์ object แต่ละไฟล์เข้าด้วยกัน

gcc -o โปรแกรม.exe ไฟล์ที่1.o ไฟล์ที่2.o

ให้เราใส่ ไฟล์ .o ตามจำนวนไฟล์ .c ของโปรแกรมเรา

ตัวอย่าง สมมติว่ามีไฟล์
hellofunc.c

#include <stdio.h>
#include "hellomain.h"

void myPrintHello(void) {

  printf("Hello HAHAHA!\n");

  return;
}

hellomain.c

#include "hellomain.h"

int main() {
  // call a function in another file
  myPrintHello();

  return(0);
}

hellomain.h

void myPrintHello(void);

การใช้ GCC ถ้าเป็น windows

gcc -c hellofunc.c
gcc -c hellomain.c
gcc -o program.exe hellofunc.o hellomain.o


การใช้ GCC ถ้าเป็น linux

gcc -c hellofunc.c
gcc -c hellomain.c
gcc -o program hellofunc.o hellomain.o
./program

ก็จะได้ผลลัพธ์ตามรูป

วิธีใช้ GCC กับภาษา C ด้วย -Wall

สำหรับคำสั่งของ GCC ที่ใช้การ compile ใน ภาษา C ที่สำคัญและมีประโยชน์อีกคำสั่งนั้นก็คือ –Wall
โดย –Wall นั้นหมายความวว่าเราต้องการให้ GCC แจ้งเตื่อน Warning ทั้งหมด หรือ Show warning all นั้นเอง

ตัวอย่างของ การใช้ –Wall และผลลัพธ์ของการเตื่อน Warning

helloworld3.c

#include <stdio.h>
#include "helloworld3.h"

int main (void)
{
    int x;

    
    printf("%s\n", HELLO);
    return 0;
}

helloworld3.h

#define HELLO "HELLO WORLD 3"

จะเห็นว่าถ้าไม่ใช้ -Wall ก็จะไม่มีคำเตื่อนอะไรเลย

ส่วนถ้าใส่ -Wall ก็จะขึ้นดังรูป

จะเห็นว่ามี Warning บอกว่า 6:9 warning : unused variable ‘x’
นั้นหมายความว่า ไม่มีการใช้งานตัวแปร x โดยตัวเลข 6:9 นั้นเป็นการบอกตำแหน่งของ warning ที่ source code บรรทัดที่ 6 คอลัมน์ที่ 9 (นับเป็น space ก็จะได้ 9 space)

วิธีใช้ GCC กับภาษา C

วิธีการ Compile ภาษา C โดยใช้ Gnu GCC compilerในกรณีที่มีไฟล์ภาษา C หนึ่งไฟล์ สามารถ Compile ออกมาเป็นโปรแกรมได้โดยใช้คำสั่งตามนี้

ในกรณี Windows (compile เพื่อให้ output ออกมาเป็นไฟล์ exe)

gcc –o ชื่อโปรแกรม.exe ชื่อไฟล์..c

หรือ

gcc ชื่อไฟล์..c –o ชื่อโปรแกรม.exe

ในกรณี Linux (compile เพื่อให้ output เป็นไฟล์ที่ไม่ต้องมีนามสกุล หรือจะเป็นไฟล์ที่มีนามสกุล.out ก็ได้เช่นกัน)

gcc –o ชื่อโปรแกรม ชื่อไฟล์..c

หรือ

gcc –o ชื่อโปรแกรม.out ชื่อไฟล์..c

หรือ

gcc ชื่อไฟล์..c –o ชื่อโปรแกรม

หรือ

gcc ชื่อไฟล์..c –o ชื่อโปรแกรม.out

จะเห็นว่าสามารถสลับตำแหน่งการเรียงคำสั่งได้


ตัวอย่างเช่นถ้าเรามีไฟล์ helloworld.c

#include <stdio.h>

int main (void)
{
    printf("HELLO WORLD\n");
    return 0;
}

Compile helloworld.c ใน Windows

gcc –o helloworld.exe helloworld.c
helloworld.exe

Complie helloworld.c ใน Linux

gcc –o helloworld helloworld.c
./helloworld

ส่วนในกรณีที่ ไฟล์ C ของเรามีการ include header file เข้ามาด้วย เวลาใช้คำสั่ง gcc ก็ไม่ต้องสนใจไฟล์ header .h ให้เราทำการ compile แค่ไฟล์ .c ตามปกติ
ตัวอย่างเช่น
helloworld2.c

#include <stdio.h>
#include "helloworld2.h"

int main (void)
{
    printf("%s\n", HELLO);
    return 0;
}

helloworld2.h

#define HELLO "HELLO WORLD 2"

Complie helloworld.c ใน Windows

gcc –o helloworld2.exe helloworld2.c
helloworld2.exe

การเก็บ Code เก่าไว้ ในภาษา C

หากมีการแก้ไข Code บางส่วน แล้วเราต้องการที่จะเก็บ Code เก่าบางส่วนเอาไว้เพื่อเอาไว้อ้างอิง ในอนาคตในภาษา C ซึ่งมีอยู่หลายวิธี

หลายๆคนอาจจะใช้วิธีการ Block comment แต่การ Block comment นั้นมีข้อเสียตรงที่ หากใน Code ของเรามี Block comment อยู่ก็จะให้เกิด syntax error ได้ ตามตัวอย่างด้านล่างนี้

#include <stdio.h>

int main(void)
{

 
 /*   /* Month */
    printf("This month is May\n");

    printf("This month is Jul\n"); */
 

    /* Day of week */
    printf("This day is Monday\n");

    return 0;
}

อีกวิธีหนึ่งก็คือการใช้ #ifdef NOT_DEFINE แล้วปิดด้วย #endif ก็สามารถทำได้เช่นกัน แต่ก็มีข้อเสียตรงที่ต้องระวังหากมีใครไปเปิดปิด ค่า Define ของเรา ก็จะทำให้ code ที่เราปิดไว้ทำงานขึ้นมาได้

จึงมีอีกวิธีหนึ่งก็คือการใช้ #if 0 แล้วปิดด้วย #endif ซึ่ง compiler จะ compile #if 0 ออกมาเป็น เท็จเสมอทำให้ code ที่เราปิดไว้ไม่ถูก compile นั้นเอง
ตัวอย่างการใช้ #if 0

#include <stdio.h>

int main(void)
{
    #if 0
    /* Month */
    printf("This month is May\n");

    printf("This month is Jul\n");
    #endif

    /* Day of week */
    printf("This day is Monday\n");

    return 0;
}

แต่ข้อเสียของ #if 0 ก็มี นั้นคือ ไม่สามารถใช้กับ code ที่เขียนไม่ถูกต้องได้ เช่น การเขียนภาษาอื่นที่ไม่ใช่ภาษา C หรือ การใส่เครื่องหมาย double quote(“) หรือ single quote (‘) ที่ไม่ครบ หรือการใช้ block comment ที่ไม่ครบ block ก็จะทำให้เวลาใช้ #if 0 แล้วทำให้ compile แล้ว error
ตัวอย่าง

Double quote(“) ไม่ครบ

#include <stdio.h>

int main(void)
{

    #if 0
    /* Month */
    printf(This month is May\n");

    printf("This month is Jul\n");
    #endif

    /* Day of week */
    printf("This day is Monday\n");

    return 0;
}

Block comment ไม่ได้ปิดท้าย

#include <stdio.h>

int main(void)
{

    #if 0
    /* Month
    printf("This month is May\n");

    printf("This month is Jul\n");
    #endif

    /* Day of week */
    printf("This day is Monday\n");

    return 0;
}

อีกวิธีก็คือการใช้ Line comment ข้อเสียของวิธีนี้ก็คือ ต้องใช้ความสามารถของ IDE หรือ Text editor ช่วยในการ comment ในกรณีที่ code มีหลายๆบรรทัด ซึ่งส่วนใหญ่จะมีฟังชั่นเหล่านี้อยู่แล้ว
Line comment short cut ใน Sublime Text ก็คือ (Ctrl + /)
Line comment short cut ใน Notepad++ ก็คือ (Ctrl + k)
ตัวอย่าง Line comment

#include <stdio.h>

int main(void)
{


    // /* Month*/
    // printf("This month is May\n");

    // printf("This month is Jul\n");


    /* Day of week */
    printf("This day is Monday\n");

    return 0;
}

ดังนั้นหากจะต้องการที่จะเก็บ Code เก่าบางส่วนเอาไว้เพื่อเอาไว้อ้างอิง ก็ขอแนะนำให้ใช้ Line comment หรือหันมาใช้พวก Version control ดีกว่า

ขนาดของตัวแปรใน ภาษา C ในแต่ละ CPU

ขนาดของตัวแปรใน ภาษา C หรือ C++ นั้นจะขึ้นอยู่กับชนิดของ CPU และ Complier ซึ่งอาจจะเหมือนกันหรือต่างกันก็ได้ เรามาดูกันดีกว่าว่าแต่ละ CPU มีการกำหนดขนาดตัวแปรที่แตกต่างกันอย่างไร

ขนาดของตัวแปรใน x86

ขนาดของตัวแปร ใน CPU ARM 32 bit

อ้างอิงจาก ARM Compiler Reference

ขนาดของตัวแปรใน MCU TI MSP430

อ้างอิงจาก slaa534 MSP430 Embedded Application Binary Interface

จะเห็นได้ว่า แต่ละ CPU จะมีขนาดของตัวแปรที่กำหนดแตกต่างกันไป ดังนั้นการเขียนโปรแกรมในภาษา C หรือ C++ แล้วนำเอาไปใช้งานในแต่ละ CPU จึงต้องระมัดระวัง เนื่องจากขนาดของตัวแปรที่แตกต่างกันอาจจะทำให้โปรแกรมของเราทำงานได้ไม่ถูกต้องก็ได้

ค่าสูงสุดและต่ำสุดของตัวแปร Real ในภาษา C

เราสามารถหาค่าสูงสุดและต่ำสุดของตัวแปร ตัวเลขจำนวนจริง (Real) แต่ละชนิดในภาษา C ได้จากค่า Define ที่อยู่ใน ไฟล์ float.h

ตารางด้านล่างนี้แสดง Define ที่ใช้สำหรับกำหนดค่าสูงสุด (Max) หรือต่ำสุด (Min) และความละเอียด (Precision)

ซึ่งสามารถนำเอาค่า Define มา print แสดงค่าได้ โดย Include ไฟล์ float.h ดัง Source code ตัวอย่าง

#include <stdio.h> /* For command line input and output */
#include <float.h> /* For limits on floating-point types */

int main(void)
{
    printf("min float is %.6e\n", FLT_MIN);
    printf("max float is %.6e\n", FLT_MAX);
    printf("float provide %u decimal digits precision.\n\n", FLT_DIG);

    printf("min double is %.15e\n",DBL_MIN);
    printf("max double is %.15e\n", DBL_MAX);
    printf("double provide %u decimal digits precision.\n\n", DBL_DIG);

    printf("min long double is %.18Le\n", LDBL_MIN);
    printf("max long double is %.18Le\n", LDBL_MAX);
    printf("long double provide %u decimal digits precision.\n",LDBL_DIG);

    return 0;
}

จะได้ผลลัพธ์ดังนี้ (Windows 7 32 bit, gcc 4.9.2)

สำหรับใครที่ลองเอา Code ไปรันแล้วเจอค่าขึ้น -1.#QNAN e+000 ดังรูป

วิธีแก้ไขก็คือเวลาที่เรา Compile ให้กำหนด standard ด้วย เช่น –std=c99 เวลา compile กับ gcc

สำหรับคนที่ใช้ Code Block ก็ให้เข้าไป Setting ค่าที่ setting complier ให้ ติ๊กถูก “Have gcc follow the 1999 ISO C language standard [-std=c99]”

ค่าสูงสุดและต่ำสุดของแต่ละตัวแปร integer ในภาษา C

เราสามารถหาค่าสูงสุดและต่ำสุดของตัวแปร ตัวเลขจำนวนเต็ม (Integer) แต่ละชนิดในภาษา C ได้จากค่า Define ที่อยู่ใน ไฟล์ limit.h
ตารางด้านล่างนี้แสดง Define ที่ใช้สำหรับกำหนดค่าสูงสุด (Max) หรือต่ำสุด (Min)

ซึ่งสามารถนำเอาค่า Define มา print แสดงค่าได้ โดย Include ไฟล์ limit.h ดัง Source code ตัวอย่าง

#include <stdio.h>
#include <limits.h> /* For limits on integer types */

int main(void)
{
    //unsigned char
    printf("min unsigned char = 0\n");
    printf("max unsigned char max = %u\n",UCHAR_MAX);
    printf("size unsigned char = %d\n\n", sizeof(unsigned char));
     //char
    printf("min char = %d\n",CHAR_MIN);
    printf("max char = %d\n", CHAR_MAX);
    printf("size char = %d byte\n\n\n",sizeof(char));

    //unsigned int
    printf("min unsigned int = 0\n");
    printf("max unsigned int  = %u\n", UINT_MAX);
    printf("size unsigned int  = %d\n\n", sizeof(unsigned int));
    //int
    printf("min int = %d\n", INT_MIN);
    printf("max int = %d\n", INT_MAX);
    printf("size int = %d byte\n\n\n", sizeof(int));


    //unsigned short
    printf("min unsigned short = 0\n");
    printf("max unsigned short = %u\n", USHRT_MAX);
    printf("size unsigned short = %d\n\n", sizeof(unsigned short));
    //short
    printf("min short = %d\n",SHRT_MIN);
    printf("max short = %d\n",SHRT_MAX);
    printf("size short = %d byte\n\n\n",sizeof(short));
    

    //unsigned long
    printf("min unsigned long = 0\n");
    printf("max unsigned long =  %lu\n",ULONG_MAX);
    printf("size unsigned long = %d\n\n",sizeof(unsigned long));
    //long
    printf("min long = %ld\n",LONG_MIN);
    printf("max long = %ld\n",LONG_MAX);
    printf("size long = %d byte\n\n\n", sizeof(long));


    //unsigned logn long
    printf("min unsigned long long = 0\n");
    printf("max unsigned long long = %llu\n", ULLONG_MAX);
    printf("min unsigned long long = %d\n\n", sizeof(unsigned long long));
    //logn long
    printf("min long long = %lld\n", LLONG_MIN);
    printf("max long long = %lld\n", LLONG_MAX);
    printf("size long long = %d byte\n\n\n", sizeof(long long));
    
    return 0;
}

จะได้ผลลัพธ์ดังนี้ (Windows 7 32 bit, gcc 4.9.2)

min unsigned char = 0
max unsigned char max = 255
size unsigned char = 1

min char = -128
max char = 127
size char = 1 byte

min unsigned int = 0
max unsigned int = 4294967295
size unsigned int = 4

min int = -2147483648
max int = 2147483647
size int = 4 byte

min unsigned short = 0
max unsigned short = 65535
size unsigned short = 2

min short = -32768
max short = 32767
size short = 2 byte

min unsigned long = 0
max unsigned long = 4294967295
size unsigned long = 4

min long = -2147483648
max long = 2147483647
size long = 4 byte

min unsigned long long = 0
max unsigned long long = 18446744073709551615
min unsigned long long = 8

min long long = -9223372036854775808
max long long = 9223372036854775807
size long long = 8 byte

การอ้างอิงที่อยู่ของการ Include Header file

การ Include header file ที่เราสร้างขึ้นมาโดยใช้ #include “file” แต่ว่าหากเราจะอ้างอิงที่อยู่ของ header file ในกรณีที่อยู่คนละ folder นั้นเราสามารถทำได้ โดยแบ่งได้เป็น 2 แบบ

แบบที่ 1 include header file ที่อยู่ใน folder ที่อยู่ใน level มากกว่า (folder ย่อยลึกเข้าไป)
จะใช้เครื่องหมายทับ \ ในการระบุ folder ย่อยๆเข้าไป โดยยึดจุดอ้างอิงจาก file .c ของเรา
สมมติว่าเรามี File ตามที่อยู่นี้
D:\Desktop\helloworld1.c
และ
D:\Desktop\test\helloworld1.h
เราสามารถ Include ได้โดย

#include <stdio.h>
#include "test/helloworld1.h"

int main (void)
{
    printf("%s\n", HELLO);
    return 0;
}

และแบบที่ 2 include header file ที่อยู่ใน folder ที่อยู่คนละ level
จะใช้เครื่องหมาย.จุดจุด .. ในการระบุ folder ที่ต้องออกมา 1 ชั้น โดยยึดจุดอ้างอิงจาก file .c ของเรา เช่นกัน

สมมติว่าเรามี File ตามที่อยู่นี้
D:\Desktop\main\helloworld2.c
และ
D:\Desktop\test\helloworld2.h
เราสามารถ Include ได้โดย

#include <stdio.h>
#include "../test/helloworld2.h"

int main (void)
{
    printf("%s\n", HELLO);
    return 0;
}

และอีกกรณี ก็คือ ต้องอ้างอิงออกไปมากกว่า 1 ชั้น โดย .. จะใช้แทนการอ้างอิงออกจาก folder นั้น 1 ชั้น
D:\Desktop\main\submain\helloworld3.c
และ
D:\Desktop\test\helloworld3.h

#include <stdio.h>
#include "../../test/helloworld3.h"

int main (void)
{
    printf("%s\n", HELLO);
    return 0;
}

หลักการใช้ #include

เราสามารถ แบ่งการ Include ในภาษา C ได้เป็น 2 แบบ นั้นก็คือ

#include <file>

และ

#include "file"

สังเกตได้ว่าจะแตกต่างกันที่เครื่องหมาย “” และ < >

เราจะใช้ < >

#include <file>

สำหรับการ include ไฟล์ ที่อยู่ใน folder ของ compiler เช่น header files (.h) ที่เป็นมาตรฐาน string.h, stdio.h, math.h หรือ library ที่เป็นมาตรฐานมากับ compiler อยู่แล้ว เหล่านี้เป็นต้น

ส่วน “”

#include "file"

จะเป็นการ include header file ที่ไม่ได้อยู่ใน folder compiler หรือ เป็น header file (.h) ที่เราสร้างขึ้นมาเอง

การใช้ #include ที่ไม่ถูกต้องอาจจะทำให้ compile แล้วเกิด warning หรือ error ก็ได้ขึ้นอยู่กับ compiler แต่ละตัว ดังนั้นควรจะใช้ #include ให้ถูกต้องกันด้วยละ