History
C was developed by Dennis Ritchie at Bell Labs in the early 1970's . . It is a general purpose procedural programming language used in a variety of different applications. The language was developed at the same time that the Unix operating system was being developed and the language was used to develop the Unix utilities and later on used in the development of the kernel. It is somewhere between a high level and a low level language. There are compilers for it for every major operating system. It's syntax and grammar has been used in many languages since then. Some examples are: Java, Object-C, C#, C++, D, Go .
Let us study how a computer program is executed on a computer. The diagram is somewhat simplistic and offers an overview of how a computer will work in general.
The instructions for the program are stored in the RAM and the CPU grabs an instruction from the RAM and executes it. It will then grab the next instruction and execute that and so on. What type of instructions are these ? Well these are the instructions that the CPU understands. In case of an Intel CPU the machine
language is based on x86 . That is the only thing the CPU understands. It does not know about C++ , Java or any other language. It only knows about the x86 machine language. What do the instructions of this machine language look like ?
000000 00001 00010 00110 00000 100000
100011 00011 01000 00000 00001 000100
As you can imagine writing machine language instructions is a tedious and impractical
task. Programmers used Assembly language instead to do their programming. So
how did the CPU run Assembly language .
Well it didn't. The Assembly language program was converted to machine language
by a compiler.
What did the Assembly language instructions look like ?
MOV AL, 1h ; Load AL with immediate value 1
MOV CL, 2h ; Load CL with immediate value 2
MOV DL, 3h ; Load DL with immediate value 3
The Assembly language instruction had one to one correlation with machine language
and was slightly better in terms of using opcodes but still a tedious task. So now the
high level languages came such as Fortran, Pascal, C . For a long time programming
was done in these high level languages . These language had features like functions,
procedures , structures. A programmer could use variables and did not have to worry
about mapping variables to specific addresses in memory.
However these languages involved the programmer converting the application problem
in terms of the language which still bore a close relationship to the way the CPU
worked. The concept of object oriented languages was developed and thus came
languages such as Java, C# and C++ . Now a programmer could translate the
application problem in terms of objects and classes. The way we think about problems
and concepts can be correlated and organized better with object oriented programming
. As an example think of an application for the bank . We can think of a customer with
properties such as name, age, gender, address, phone. This customer could have an
account and the account could be checking or saving. An account can have properties
like amount and account number. It could also have methods like add a deposit and so
on. This sort of technique can be used in many problems where we can model the
solution in terms of classes to more closely resemble the problem domain.
C++ is based on the "C" language that was created by Dennis Ritchie in the late 60's.
The "C" language was a procedural based language . Object oriented features were
added to it and C++ was born. C++ is a super set of the "C" language and most "C"
programs can run under "C++". Both the languages involve a steep learning curve.
We are going to write our first program, compile and run it under different operating systems. As is the convention our first program is going to print "Hello World" to the console and that's all it does. It is recommended that the compilation be done with command line tools such as"gcc" instead of using a an IDE/GUI tool . Also it is recommended that the user be familiar with basic unix commands and the "vi" editor.
Windows
On the Windows operating system we have many options in order to compile and run it.
Cygwin
One option is to install the Cygwin software from the site:
Cygwin is a Unix simulator for Windows. We can think of it as an application like "Notepad" or "Word" . It is not a virtual operating system or a tool that remotely logs in to another system.
Choose the "g++" or "gcc" compiler and the "make" utility from the options when installing. For a text editor we have many options available . One editor is "Textpad" that can be downloaded from the site:
You should also download the syntax definition file from the "Textpad" site for C language. That will enable the syntax coloring .
C Programs are text files and can be created with any text editor. On Windows you can use "Notepad" , "TextEdit", "Sublime" . On Unix, Cygwin you can also use the "vi" editor . You cannot use an editor that is not plain text and adds other markup stuff such as Microsoft word or Wordpad.
Now the "Hello World" program:
File: "hello.c"
#include <stdio.h>
int main()
{
printf( "Hello, World!" ) ;
return 0 ;
}
Save this to a file. You can name it to be "hello.c" but it can be anything you like. Then from the "Cygwin" command line, navigate to the folder where you created "hello.c" and type in:
gcc hello.c
Then do a "ls" and you should see a file "a.exe" or "a.out" . This is the executable that got created.
We can run this executable by the command :
./a.out
and you should see the following output on the console:
Hello World
Eclipse and Netbeans
Other IDE environments that can be installed are Eclipse and Netbeans but these require a compiler such as "g++" to be installed separately. Microsoft has an IDE called "Visual Studio Express" that can be downloaded for free. There are some restrictions on this particular version but it can be used for learning purposes.
Apple
On the Mac there is software by Apple called Xcode . You need an Apple developer account and it's free. There are 2 versions of it. One includes an IDE and the other is command line based. The Mac is essentially a Unix based system under the hood. Open the "Terminal" application and then create the file "hello.c" in a folder. It can be any folder. Then follow the same steps as above for "Cygwin" .To check if the "C" compiler is already installed on the system we can type "gcc" on the console. If you get an error of the sort "input file not found" the "C" compiler is installed and if you get "command not found" error then the "C" compiler is not installed.
Unix
Unix systems vary in the setup of the C compiler. You can "google" for instructions on how to install the "C" compiler for your system.
A C program must have the "main" function. This is where the execution starts. It can be composed of a single file or multiple files. Usually the extension of the file is "*.c" and the extension of the header file is "*.h" . The header files are not used in the compilation step but in the preprocessor step. Just like other languages there is a grammar and rules for writing C++ code. We have keywords, variable names, functions. If a C program has syntax errors then the compiler will throw out errors.
File: "hello.c"
#include <stdio.h>
int main()
{
printf ( "Hello, World!" ) ;
return 0 ;
}
Every C program must have a main method. That is where the execution starts from. Any functions or variables that are used from the library must be declared. That is where the "include" files come in. Any statement with a "#" tag is first handled by a part of the compiler called the preprocessor. We shall study the preprocessor later on. The "printf" function prints the string "Hello World" to the console. At this stage we are only doing an overview. The structure and detailed information about functions and the "printf" call will be covered later on.
Let's say we compile the above program on Windows and we get an exe named "hello.exe" . Can we take this "exe" and run it say on a Mac or Windows machine ? No we can't. Even if the processors are the same ( Intel compatible ) an operating system has it's own format for executable's . Specific operating system information is inserted into the executable making it impossible to run on other systems.
You can read more on this topic at the below link :
https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats
Let's study the compilation process in a bit more detail. We know that the compiler will convert our code to the machine language instructions so that the CPU can run the program. Let's work with the file that we used:
File: "hello.c"
#include <stdio.h>
int main()
{
printf( "Hello, World!" ) ;
return 0 ;
}
We compiled it with the command:
gcc hello.c
and that produced an executable. We can give the name of the executable using the "-o" option.
gcc -o hello hello.c
$ ./hello
Hello, World!
In the above the executable file named "hello" got created and we can run it with the command "./hello" .
If there is a syntax mistake then the compiler will give us an error.
File: "hello1.c"
#include <stdio.h>
int main()
{
printf("Hello, World!" )
return 0;
}
Output:
$ gcc hello1.c
hello1.c: In function ‘main’:
hello1.c:7:4: error: expected ‘;’ before ‘return’
return 0
In the above program we introduced an error by removing the ";" at the end of "printf" . The compiler caught the error and stated the error.
The way the compiler works is it compiles file by file . Here we only have 1 file. The compilation starts at the top of the file and proceeds to the bottom. The preprocessor does it's task first and then the compiler creates an object file that corresponds to the "hello.c" . The linker ( another part of the compiler ) takes this object file and the creates the final executable file. In the above all of these steps happened behind the scenes. We can take this longer approach to create our executable file.
File: "hello2.c"
#include <stdio.h>
int main()
{
// printf() displays the string inside quotation
printf("Hello, World!") ;
return 0 ;
}
First we are going to create an "object" file .
This can be done with the "-c" option.
$ gcc -c hello2.c
If we do a "ls" we will notice that a new file "hello.o" has been created. What is in this file ? Let us print the file out using the command "cat" . We will see a bunch of gibberish characters because the object file contains machine language instructions. Let us try to run the file:
$ ./hello2.o
-bash: ./hello2.o: Permission denied
The "gcc" Unix compiler will add "execute" permissions when it decides that a file should be run and in this case it didn't do that. Let's do that ourselves. There are 2 ways to add permissions to a file in Unix and we shall choose the binary bit method.
$ chmod 777 hello2.o
$ ./hello2.o
-bash: ./hello2.o: cannot execute binary file: Exec format error
We cannot execute the object file even though it is machine instructions . We now use the linker part of the compiler to create the executable file.
$ gcc -o hello2 hello2.o
$ ./hello2
Hello, World!
We used the same flag "-o" to specify the name of the executable but we did not specify the ".c" file but instead specified the name of the executable file that we can run. So what's the difference between the object and the executable files ? Both contain machine language instructions . The "object" file does not check if a function has a body and it also does not check if it has a "main" function. Let's try another example called "hello3.c"
File: "hello3.c"
#include <stdio.h>
int main1()
{
printf("Hello, World!");
return 0;
}
We renamed the "main" function to be "main1" . Let us compile this to the object file.
gcc -c hello3.c
We do not receive any errors and an object file "hello3.o" is created. Now let's try to create the executable.
$ gcc -o hello3 hello3.o
On Cygwin we receive the following errors:
/usr/lib/gcc/x86_64-pc-cygwin/7.4.0/../../../../lib/libcygwin.a(libcmain.o): In function `main':
/usr/src/debug/cygwin-3.1.2-1/winsup/cygwin/lib/libcmain.c:37: undefined reference to `WinMain'
/usr/src/debug/cygwin-3.1.2-1/winsup/cygwin/lib/libcmain.c:37:(.text.startup+0x7f): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `WinMain'
collect2: error: ld returned 1 exit status
Essentially the compiler is stating that it can't create the executable because we don't have a function by the name of "main" . Remember "main" is the entry point for a "C" program and when we run the executable then the executable needs to know where to start the program. This is one of the differences between object files and executable files. The linker checks for the "main" function and complains when it doesn't find it. The other difference is that the linker will check for the definition of functions but the object file won't. This will be explained more in the below section dealing with multiple files. The importance of why we have the "object" concept will also come clear.
Multiple Files
We have seen how to compile a single "C" file"; however a single "C" program can be spread over multiple files. In fact a single program in the industry will usually be composed of hundreds of files and even thousands. Our next example is going to consist of 2 files: "main.c" and "myprint.c" .The file "myprint.c" is going to print the "Hello World" to te screen .
File: "main.c"
int main()
{
typeToConsole() ;
return 0 ;
}
File: "myprint.c"
#include <stdio.h>
void typeToConsole()
{
printf("Hello, World!");
}
We can compile multiple files using the following command:
$ gcc main.c myprint.c
main.c: In function ‘main’:
main.c:6:4: warning: implicit declaration of function ‘typeToConsole’ [-Wimplicit-function-declaration]
typeToConsole() ;
We get a "warning" . Let us study why we got this warning . The compilation process in "C" is file by file. All the files are not handled at once. The compiler looks at the file "main.c" and starts going from top to bottom. It encounters the method "typeToConsole" and the warning it produced. But we state that the method is defined in "myprint.c" . Yes that is true but the compiler has not gotten to that file yet. Remember it looks at the program file by file. The function "typeToConsole()" is defined in a different file. The declaration of a function is the name of the function and what parameters it takes and what it returns. The declaration for this function is:
void typeToConsole() ;
Notice there is a semi colon after it and there is no body. If there is a body then we say that function has a definition such as:
void typeToConsole()
{
printf("Hello, World!");
}
We can have multiple declarations in a single program but only one definition. We need to place a declaration in the "main.c" . That tells it what the parameters are if any and the compiler can check that it is being made correctly. It does not have the body at this point but it does not need it.
Our new "main.c" program:
File: "main.c"
void typeToConsole() ;
int main()
{
typeToConsole() ;
return 0 ;
}
$ gcc main.c myprint.c
$ ./a.exe
Hello, World!
We do not get any compiler errors and get a successful output. We needed to add the declaration "typeToConsole" in the file "main.c" in order to avoid the warning. What if there is another file say "some1.cpp" that needs to call the "typeToConsole()" function ? We can add the declaration in that file just as we did in "main.c"" but "C" gives us a better way to do this. We place the declaration in another file ending with an extension ".h" such as "myprint.h" and include that file wherever we need to call the function "typeToConsole" function. Our program with the 3 files looks like this.
File: "main.c"
#include "myprint.h"
int main()
{
typeToConsole() ;
return 0 ;
}
File:"myprint.h"
void typeToConsole() ;
File: "myprint.c"
#include <stdio.h>
void typeToConsole()
{
printf("Hello, World!");
}
$ gcc main.c myprint.c
$ ./a.exe
Hello, World!
We placed the following line in "main.c"
#include "myprint.h"
There is a component of the compiler called the "Preprocessor" named because it does certain things before the translation and conversion to machine language instructions is done. If the "preprocessor" sees that the file has the hashtag character then it works on that and creates a new source file beneath the scenes. That new file is then compiled. The "#include" locates the header file and literally copies it's contents into the ".c" file just as if we had done a manual copy and paste. We can check what the "Preprocessor" does with the "E" option.
$ gcc -E main.c
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.c"
# 1 "myprint.h" 1
void typeToConsole() ;
# 3 "main.c" 2
int main()
{
typeToConsole() ;
return 0 ;
}
We can see that the declaration was placed above the "main" function . Now if any other file wants to use the "typeToConsole()" all they have to do is include the file "myprint.h" . If there were multiple function calls in the file
"myprint.cpp" then all those function declarations can be placed in the file "myprint.h" an any ".c" file can then include the header file and use those functions.
We have 2 ".c" files in the above example. Notice we do not have to give the header file in the "gcc" command. The source files and the "Preprocessor" bring the information from the header file in. What about object files and stuff. This is how the internal actions take place.
gcc main.c myprint.c
The "Preprocessor" modifies the file "main.c" and "myprint.c" .
The object files are produced.
main.o myprint.o
Then the linker kicks in and creates a single executable file "a.exe" . We can replicate this process with the following commands.
gcc - c main.c
gcc -c myprint.c
The 2 object files can be linked with the following command.
gcc main.o myprint.o
Let's say we make change in the file "main.c" . Using the above approach we can recompile the "main.c" file to the "main.o" file.
gcc -c main.c
and the use the linker :
gcc main.o myprint.o
Notice we did not have to recompile the "myprint.c" . This is where the "object" files come in useful. The compilation step is usually time consuming as it has to check the syntax errors. For 2 or 3 files it may not make much of a difference but if there are hundreds of files then this can be a difference between few minutes and few hours to produce the executable.
Top down compilation
File: "main.c"
#include <stdio.h>
int main()
{
typeToConsole() ;
return 0 ;
}
void typeToConsole()
{
printf("Hello, World!");
}
What happens if we compile the above code ? We get a warning message about the function "typeToConsole". The function is defined in the same file but the compiler complains about not finding the function "typeToConsole".
Remember the compiler starts looking at the file from top to bottom. It encounters the function "typeToConsole" in the function "main" but has not seen the definition yet so complains. We need to again use the same trick of placing the declaration first.
File: "main.c"
#include <stdio.h>
void typeToConsole() ;
int main()
{
typeToConsole() ;
return 0 ;
}
void typeToConsole()
{
printf("Hello, World!");
}
Now the compiler does not complain.
Make Utility
Unix has a tool to help us with the compilation process. It is the "make" utility. If you get a project dealing with "C" programs on a Unix system it is very likely that the make utility is being used to manage the project. The "make" utility is not just used for "C" programs but is a general and useful tool to manage projects.
Le'ts use the "make" tool to compile our "hello world" program.
File: "hello.c"
#include <stdio.h>
int main()
{
printf( "Hello, World!" ) ;
return 0 ;
}
In the same folder we create a text file called "Makefile" with the following contents.
hello: hello.c
gcc -o hello hello.c
We then run a command called "make" from the command line and that produces our executable. The entry in the "Makefile" is called a task. A task has 3 components.
Target: Dependencies
Command/Commands
On the first line to the left we have the target. This is the file that we are interested in. To the right we have all the dependency files and the next line contains the command that we want to run. The second line is indented using tabs and not spaces. This is an easy mistake to make. When we run the "make" command the utility looks for a Makefile and starts to execute the first task in it. It then checks if the file "hello" exists or not. In our case it does not exist then it checks the dependencies. It's possible the dependency can be another task. In this case it's not. The command is then executed:
gcc -o hello hello.c
This creates the executable "hello". Now let's run the "make" command again.
$ make
make: 'hello' is up to date.
The make utility looks at the task and then checks for the executable file "hello" and sees that it does exist. It then checks to see if the dependency needs is a task and since it isn't it checks if the file has been updated since the last time the "hello" was created and since it hasn't it concludes that there is no need to do a build again. The utility is intelligent enough to keep track of times . Let us make a superficial change to the file "hello.c" by adding a blank space and saving it. We run the "make" command again and the command is run because the dependency has changed since the last time. This was a simple case of using make. Let us use the "make" tool to work on the multiple file example that we did earlier on. We had 3 files: "main.c" , "myprint.c" and "myprint.h" .
Makefile:
main: main.o myprint.o
gcc -o main main.o myprint.o
main.o: main.c myprint.h
gcc -c main.c
myprint.o: myprint.c
gcc -c myprint.c
Our "Makefile" is composed of several tasks. Our first task states that we are interested in the target "main" and the dependency is "main.o" and "myprint.o" that happen to be other tasks. The first time "make" is run it will check for the executable file "main" and see that it does not exist so it realizes that the command for this task which is "
gcc -o main main.o myprint.o" needs to be executed but it has to resolve the dependencies first. The first is "main.o" which is the second task and it goes there and sees that "main.o" does not exist . Next it checks it's own dependencies and sees that they are not tasks. Now it executes it's own command "gcc -c main.c" . Similarly the
next task "myprint.o" is evaluated and it's command is run and finally the command "gcc -o main main.o myprint.o" is run. We have gotten "make" to do what we had in mind even though the "make" utility does not know anything about the "C" compiler or has any notion of object , executable files. If we change the file "myprint.c" then the "main.o" file will not be created again.
$ make
gcc -c myprint.c
gcc -o main main.o myprint.o
The target in a task does not have to a file that will be created by the make utility. It is normal to see tasks like "clean" even though a file called "clean" will never be created. Let us create such a task in our Makefile that will be used to delete all the object and executable files.
Fie: "Makefile"
main: main.o myprint.o
gcc -o main main.o myprint.o
main.o: main.c myprint.h
gcc -c main.c
myprint.o: myprint.c
gcc -c myprint.c
clean:
rm *.o ; rm main
We now have an additional task called "clean" . It does not have any dependencies associated with it. The command on the second line will not create this "clean" file so the command is always going to be executed. Using a semicolon we can place more than 1 command as we have done here.
$ make clean
rm *.o ; rm main
We only want to execute the "clean" task. If we just run "make" then the first task in the "Makefile" starts to execute. We can give the name of the task in the command line itself . The text file does not have to be "Makefile" .
Below is a general "Makefile" that can be used to compile and run a general single file C program.
$(ARGS).exe: $(ARGS).c
gcc -o $(ARGS).exe $(ARGS).c ; ./$(ARGS).exe
clean:
rm *.exe ; rm *.o
The "make" utility is very big and books have been written on it. Our aim was to introduce it to give an idea of what it is.
Let's assume we have 2 files in our project.
File: "main.c"
int main()
{
g1 = 5 ;
}
File: "another.c"
int g1 ;
When we try to compile the 2 files we will receive an error:
$ gcc main.c another.c
main.c: In function ‘main’:
main.c:3:5: error: ‘g1’ undeclared (first use in this function)
g1 = 5 ;
^~
main.c:3:5: note: each undeclared identifier is reported only once for each function it appears in
We know that if a variable is defined in another file then it can't be seen automatically by another file. We can try to declare the variable "g1" just as we did for functions.
File: "main.c"
int g1 ;
int main()
{
g1 = 5 ;
}
However this creates a problem. We have not declared the variable "g1" but defined it. We need a way to tell "C" that we are not defining the variable and this is where the keyword "extern" comes in. The proper wasy is as below.
File: "main.c"
extern int g1 ;
int main()
{
g1 = 5 ;
}
File: "another.c"
int g1 ;
We work with numbers using base 10 or the decimal system. When we have a number such as
245
This is interpreted as :
2 * 10 to power 2 + 4 * 10 to power 1 + 5
If we have a base such as 10 then the digits range from 0 to 9 ( 0 to base -1 ) . When we go from right to left we
multiply the digit by the base to the power increasing the power by 1 each time.
Computer work with base 2 also called the binary system. The digits are 0 and 1 . The number
101
is 5 in decimal system( 1*2 power2 + 0 + 1 ) .
If we have 3 digits then the unsigned number will look like below:
000 -- 0
001 -- 1
010 -- 2
011 -- 3
100 -- 4
101 -- 5
110 -- 6
111 -- 7
Two's complement.
A negative binary number is represented in two's complement. In this scheme the leftmost bit is "1" and represents the negative number. The right most bits are inverted and then a "1" is added to get the negative number amount.
Ex:
101
The bit "01" are inverted to form "10" and then a "1" is added to form "11" so the number "101" is actually "-3" . If we have 3 digits then the signed numbers look as below:
000 0
001 1
010 2
011 3
100 -4
101 -3
110 -2
111 -1
Base 16 also called Hexadecimal system . The digits are 0-9, A-F with A representing 10 and F representing 15.
Examples:
F0 - 240
AF - 175
The computer stores numbers in binary. The smallest unit is called 1 bit that can be 0 or 1 .
1 Byte = 8 Bits
1 Kb = 1,024 Bytes
1 Mb = 1,024 Kb
1 Gb = 1,024 Mb
File: "byte1.c"
#include<stdio.h>
int main()
{
char x1 ;
x1 = 127 ;
x1 = x1 + 1 ;
printf("x1 value is %d\n" , x1 );
return ( 0 ) ;
}
Output:
$ gcc byte1.c ; ./a.exe
x1 value is -128
The value of x1 should have been 128. What's going on ?
The Unix gcc compiler also has a debugger facility. Let's take the below
program and use the "gdb" debugger with it.
"main.c"
#include <stdio.h>
int main()
{
int i1 = 5 ;
i1 = i1 + 2 ;
i1 = i1 + 3 ;
i1 = i1 + 4 ;
return 1 ;
}
To work with the debugger we need to use different compiler option.
gcc -g main.c
and to run the gdb:
gdb ./a,exe
We can then set break points:
break 1
The command to step through is:
step
The command to run is:
run
We can also print values with the print command:
print i1
The debugger in an IDE will be much easier to use in terms
of the graphical interface than the gdb. However gdb provides
the functionality that one expects from a normal debugger.
The command "ls" will list the files and folders in our current directory.
Ex: ls
Ex: ls -l
ls with the "-l" option produces a detailed list of files.
ls -ltr will produce a long listing with files sorted by time.
pwd
The command "pwd" lists the current directory we are in ( Present Working Directory ) .
mkdir
The command "mkdir" can be used to create a folder. Ex:
mkdir demo
cd
Changes your current folder. Ex:
cd demo
cd .. //Goes one folder up
cd //Goes to the default home folder
cd ~ //Same thing changes to the default home folder
touch
The command "touch" is used to update the access date/time of a file. It can also be used to create an empty file. Ex:
touch hello.cpp
rm
Removes a file. Use rm with options to directory and all it's contents inside it.
Ex: rm "test1"
Ex: rm -rf someFolder
cp
Copies a file to some new file.
Ex:
cp hello1.cpp hello2.cpp
mv
Moves a file to another location; deleting the original file. Can also be used to rename the original file in the same folder.
mv hello1.cpp hello2.cpp
cat
The "cat" command can be used to print the contents of a file. It can also print contents of multiple files.
tar
The "tar" command can be used to compress and extract files. To compress the folder "mymath" to a file called "mymath.gz"
tar -czvf mymath.gz mymath
To extract the compressed file use:
tar -xvf mymath.gz
This will create a folder "mymath" in the current directory and extract all the files there.
chmod
A Unix file has permissions associated with it .There are permissions for reading, writing and executing the file. These 3 permissions can be set for 3 different sets of people: the owner ( creator of the file ) , the users in the group that the owner belongs to and the third set everyone else.
The permissions for a file can be viewed with the command "ls -l" .
[amittal@hills unix_stuff]$ ls -l
total 0
-rw------- 1 amittal csdept 0 Jan 26 12:10 first.txt
The first character will tell us if the file is a director ( with the "d" character ) or "-" if it is a normal file. Then we have "r", "w" and ""x" . For the above case the owner has permissions to read and write to the file. There are 2 ways we can manipulate the permissions. The first is with characters and the second using binary numbers.
[amittal@hills unix_stuff]$ chmod o+rwx first.txt
[amittal@hills unix_stuff]$ ls -l
total 0
-rw----rwx 1 amittal csdept 0 Jan 26 12:10 first.txt
The format is:
chmod people (+/-) rwx
Where people is "o" for others , "g" for group and "u" for the owner.
r - Read w - Write x - Execute u - The owner of the file g - The group that the file belongs to o - Anybody who is not one of the above a - All users + - Add permissions - - Remove permissions
[amittal@hills unix_stuff]$ chmod a+rwx first.txt[amittal@hills unix_stuff]$ ls -l total 0-rwxrwxrwx 1 amittal csdept 0 Jan 26 12:10 first.txt
The above changes permissions for all the 3 sets of people by giving everyone read, write and
execute permissions.
We can also use binary numbers.
: The first octet represents permissions for the owner. r w x T : The second octet represents permissions for the group. Owner: 4 2 1 7 : The third octet represents permissions for everyone else. Group: 0 0 0 0 : For each octet, start at 0 and: Other: 0 0 0 0 : +4 for read permission. : +2 for write permission. Command: chmod 700 : +1 for execute permission.
[amittal@hills unix_stuff]$ chmod 000 first.txt
[amittal@hills unix_stuff]$
[amittal@hills unix_stuff]$
[amittal@hills unix_stuff]$ ls -l
total 0
---------- 1 amittal csdept 0 Jan 26 12:10 first.txt
[amittal@hills unix_stuff]$ chmod 605 first.txt
[amittal@hills unix_stuff]$ ls -l
total 0
-rw----r-x 1 amittal csdept 0 Jan 26 12:10 first.txt
1)
Create a folder called "make" . Place the file "hello.c" in this folder.
File: "hello.c"
#include <stdio.h>
int main()
{
printf ( "Hello, World!" ) ;
return 0 ;
}
Create another file called "make1.txt" and create a task in it to compile the program to an executable. The
name of the executable will be "hello.exe" and the command is:
gcc -o hello.exe hello.c
Run the make utility using the command.
make -f make1.txt
Create another file "make2.txt" in the same folder. This file should contain 2 tasks to compile the file "hello.c" using the approach of creating the object file and then linking to create the executable. Run the file using the command:
make -f make2.txt
2)
Convert the hexadecimal "DA" to decimal.
Convert the binary "1011" to decimal .
Convert the decimal number 15 to binary.
1)
File: "make1.txt"
hello.exe: hello.c
gcc -o hello.exe hello.c
File: "make2.txt"
hello.exe: hello.o gcc -o hello.exe hello.o hello.o: hello.c gcc -c hello.c
2)
Convert the hexadecimal "DA" to decimal.
13 * 16 + 10 = 218
Convert the binary "1011" to decimal .
8 + 2 + 1 = 11
Convert the decimal number 15 to binary.
1111