Thread: Guidance needed, is this good programming??

  1. #1
    Registered User
    Join Date
    May 2010
    Posts
    6

    Exclamation Guidance needed, is this good programming??

    Ok I'm only new to C language, and I've built a program that takes a file and computes the MD5 checksum for it. It works which is a great sign but thats not what I'm considered about. It's the issue of pointers being passed into a function, I'm still trying to get my head around the concept of pointers themselves.

    Please can someone run through my code and see that its all in good practice, or even show me a better more efficient way of using them within a function.

    Thanks in advance for all your advice.

    md5file.h
    Code:
    /*!
     *	@file		md5file.h
     *	@breif		Computes the MD5 check sum of a given file.
     *
     *	@author		Christopher Turner <[email protected]>
     *	@version	1.0
     *	@date		May 20th, 2010
     */
    
    #define FILE_READ_BUFFER_SIZE 4096 /* 4kb */
    
    /*!
     *	@fn			extern void md5_file(const char *file, const unsigned char *digest)
     *	@brief		Copies the checksum of a file into the given digest variable.
     *
     *	@param[in]	file The path to the file to compute md5 check sum.
     *	@param[out]	digest The variable in which to copy the computed digest to.
     *	@return		Returns 1 if success and 0 if error.
     */
    extern int md5_file (const char *file, char *digest);
    md5file.c
    Code:
    #include "md5file.h"
    
    #include <stdio.h>
    #include <openssl/md5.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    
    int md5_file (const char *file, char *digest) {
    	
    	MD5_CTX md5_struct;
    	unsigned char result[MD5_DIGEST_LENGTH];
    	char tmp_hex[3];
    	int file_d;
    	int i;
    	char buffer[FILE_READ_BUFFER_SIZE];
    	ssize_t bytes_read;
    	
    	/* Initialize the md5 structure */
    	if ( MD5_Init(&md5_struct) == 0 )
    		return (0);
    	
    	/* Open the file */
    	file_d = open(file, O_RDONLY);
    	
    	if ( file_d < 0 )
    		return (0);
    	
    	/* Read the files data */
    	while (1) {
    		bytes_read = read(file_d, buffer, sizeof(buffer));
    		
    		if ( bytes_read == 0 )
    			break; /* Finished reading file */
    		
    		if ( bytes_read < 0 )
    			return (0); /* File read error */
    		
    		if ( MD5_Update(&md5_struct, &buffer, bytes_read) == 0 )
    			return (0);
    	}
    	
    	if ( MD5_Final(result, &md5_struct) == 0 )
    		return (0);
    	
    	/*  Format the digest */
    	for ( i=0; i<MD5_DIGEST_LENGTH; ++i ) {
    		sprintf(tmp_hex, "%02x", result[i]);
    		strcat((char *)digest, tmp_hex);
    	}
    	
    	close(file_d);
    	
    	return (1); /* Success */
    	
    }
    main.c
    Code:
    #include <stdio.h>
    #include "md5file.h"
    
    int main (int argc, const char * argv[]) {
    	
    	char md5_digest[33];
    	
    	md5_file(argv[1], md5_digest);
    	
    	fprintf(stdout, "%s\n", md5_digest);
    	
        return 0;
    }

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    I don't see anything particularly odd. What is it you are worried by?
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    May 2010
    Posts
    6
    Quote Originally Posted by MK27 View Post
    I don't see anything particularly odd. What is it you are worried by?
    Well I don't understand pointers well enough yet, I've been coding this for about a week now and I have had a lot of segmentation fault and abort trap responses when run, even though the build was successful.

    I guess what I'm asking is that I know when using some functions that you pass a pointer into to in order to save some value within the address its pointing to, like the second parameter in md5_file function "char *digest". Sometimes you have to pass the "&md5_digest"

    For example mine is:
    Code:
    md5_file(argv[1], md5_digest);
    but some library functions you are required do this
    Code:
    md5_file(argv[1], &md5_digest);
    what would the actual function be doing different if the second call was the case??

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Code:
    char *foo;
    char bar[32];
    In the above code, then:
    foo is of type pointer-to-char
    bar is of type pointer-to-char (array names by themselves decay to pointers to their element)
    &foo is of type pointer-to-pointer-to-char
    &bar is of type pointer-to-array-of-char

    What you need to pass depends on what sort of thing the library expects. Generally you pass address-of-something if the function needs/wants/is supposed to modify that thing.

    (Notice that if char ** was expected, &md5_digest would not work.)

  5. #5
    Registered User
    Join Date
    May 2010
    Posts
    6
    Quote Originally Posted by tabstop View Post
    Code:
    char *foo;
    char bar[32];
    In the above code, then:
    foo is of type pointer-to-char
    bar is of type pointer-to-char (array names by themselves decay to pointers to their element)
    &foo is of type pointer-to-pointer-to-char
    &bar is of type pointer-to-array-of-char

    What you need to pass depends on what sort of thing the library expects. Generally you pass address-of-something if the function needs/wants/is supposed to modify that thing.

    (Notice that if char ** was expected, &md5_digest would not work.)
    So I want to modify the variable

    Code:
    char md5_digest[33];
    with the function

    Code:
    int md5_file (const char *file, char *digest);
    is my way the correct/standard/advised way to do it, or would it be better practice to do it like the libraries do, by passing in a "pointer-to-array-of-char" into the function??

    If the better way is the libraries way, what alterations are need to change with the declaration of the function md5_file is "char **" wont work. Also what changes are made to the code in setting the value of the parameter, will I be using "*digest" instead of just "digest" to access and set its value??

    Once I get my head around this, I think I'll be confident in progressing. Thanks for your help!!

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    If you want to modify some chars, you need to pass a pointer-to-char. An array name already is a pointer-to-char, so you're done.

  7. #7
    Registered User
    Join Date
    May 2010
    Posts
    6
    NICE!! not to bad for my first program, better than HELLO WORLD lol

  8. #8
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by turnr View Post
    I guess what I'm asking is that I know when using some functions that you pass a pointer into to in order to save some value within the address its pointing to, like the second parameter in md5_file function "char *digest".
    You do append to digest, but you don't reassign the pointer so that's all fine.

    Sometimes you have to pass the "&md5_digest"

    but some library functions you are required do this
    Code:
    md5_file(argv[1], &md5_digest);
    what would the actual function be doing different if the second call was the case??
    &md5_digest is a pointer to a pointer, if you try that you will get a compiler error unless you also change the prototype to:
    Code:
    int md5_file (const char *file, char **digest);
    and make corresponding changes within the definition. The purpose of that would be if you wanted to do something like this:
    Code:
    *digest = malloc(some memory);
    Notice **digest is dereferenced to yield the original pointer submitted. Why not just submit the pointer itself? Because it is just a value -- a memory address. If you reassign that value, it would be the same thing as passing an int argument:
    Code:
    somefunc(int x);
    and then saying x += 10. That does not change the value of whatever variable provided the value for x, because all the function gets passed is that value. If you want to change the original variable's value, you would pass a pointer to it:
    Code:
    somefunc(int *x);
    This is sometimes (casually, in C) called "pass by reference". The value here is the address of an int variable, so you can change the value at that address and the original variable's value changes also. **digest is the same thing -- it's the address of md5_digest in main. Which is a pointer, so the value at that address is another address (actually it's not a pointer, so you could not make this work with md5_digest -- array names can be used as pointers for certain purposes, so pretend...). You can now assign to that and *md5_digest (if it were a real pointer) would be affected. Here's a simple illustration:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void test (char **p) {
    	*p = malloc(11);
    	strcpy(*p,"hello world");
    }
    
    int main () {
    	char *p;
    	test(&p);
    	puts(p);
    	return 0;
    }
    Pointers are a difficult concept for many/most people to grasp initially, but once you get it it's fairly simple. I tried a while back to write an explanation:

    Pointers: Pass by Value, Pass by Reference

    but it's unfinished, I kind of decided my approach was wrong and I haven't had time to change it -- but if you have time take a look and lemme know what you think, what is not clear, etc. Looking at it now I'm not sure what I thought was wrong earlier (of course, I understand pointers already...) maybe I should finish that off.
    Last edited by MK27; 05-21-2010 at 10:01 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  9. #9
    Registered User
    Join Date
    May 2010
    Posts
    6
    Quote Originally Posted by MK27 View Post
    You do append to digest, but you don't reassign the pointer so that's all fine.



    &md5_digest is a pointer to a pointer, if you try that you will get a compiler error unless you also change the prototype to:
    Code:
    int md5_file (const char *file, char **digest);
    and make corresponding changes within the definition. The purpose of that would be if you wanted to do something like this:
    Code:
    *digest = malloc(some memory);
    Notice **digest is dereferenced to yield the original pointer submitted. Why not just submit the pointer itself? Because it is just a value -- a memory address. If you reassign that value, it would be the same thing as passing an int argument:
    Code:
    somefunc(int x);
    and then saying x += 10. That does not change the value of whatever variable provided the value for x, because all the function gets passed is that value. If you want to change the original variable's value, you would pass a pointer to it:
    Code:
    somefunc(int *x);
    This is sometimes (casually, in C) called "pass by reference". The value here is the address of an int variable, so you can change the value at that address and the original variable's value changes also. **digest is the same thing -- it's the address of md5_digest in main. Which is a pointer, so the value at that address is another address (actually it's not a pointer, so you could not make this work with md5_digest -- array names can be used as pointers for certain purposes, so pretend...). You can now assign to that and *md5_digest (if it were a real pointer) would be affected. Here's a simple illustration:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void test (char **p) {
    	*p = malloc(11);
    	strcpy(*p,"hello world");
    }
    
    int main () {
    	char *p;
    	test(&p);
    	puts(p);
    	return 0;
    }
    Pointers are a difficult concept for many/most people to grasp initially, but once you get it it's fairly simple. I tried a while back to write an explanation:

    Pointers: Pass by Value, Pass by Reference

    but it's unfinished, I kind of decided my approach was wrong and I haven't had time to change it -- but if you have time take a look and lemme know what you think, what is not clear, etc. Looking at it now I'm not sure what I thought was wrong earlier (of course, I understand pointers already...) maybe I should finish that off.
    Yes I think I understand now passing by value and passing by reference, but only with your example. And in my books it also has a similar example to yours, but because C doesn't have strings and it uses arrays, and arrays are a decay pointer (is that the term??) themselves, can you show me an example passing a string (array of chars) by value and by reference? So I can establish when I need to use *, **, &'s.

    Yes I have a little programming knowledge with Cocoa and they use pointers with their objects, but this is just confusing the hell out of me :@

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by turnr
    but because C doesn't have strings and it uses arrays, and arrays are a decay pointer (is that the term??) themselves, can you show me an example passing a string (array of chars) by value and by reference?
    You cannot pass an array by reference. As you know, an array decays to a pointer, thus it is the pointer that is passed by value, and this simulates passing the elements of the array by reference. But there is no way by which you can simulate passing an array itself by reference: if you could, it means that you could assign to an array, which is not possible.

    By the way, C does have the notion of a string, i.e., a contiguous sequence of characters terminated by and including the first null character. What it does not have is a string type, not even as a struct from the standard library.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by turnr View Post
    Yes I have a little programming knowledge with Cocoa and they use pointers with their objects, but this is just confusing the hell out of me :@
    Yeah, I think what I didn't like about that article was this:

    If you already understand what a pointer is, you can skim the next few paragraphs, as they involve a lot of repetition. If not, re-read them slowly several times until they make sense.
    Which makes me think I am just too lazy to be simple and clear, the next paragraph is kind of dense. On the other hand, it does explain all the & * syntax and nomenclature, and you might as well re-read one paragraph 5 times as read 5 different paragraphs that draw the explanation out further.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by turnr View Post
    can you show me an example passing a string (array of chars) by value and by reference?
    Can't be done. That's what it means by "decaying to a pointer" -- there is no way to get an array into a function -- either you pass a specific element of the array to the function (which can be done either by value or by reference) or you type the array name and it decays to a pointer to the first element. Passing the address of an array (as in with an ampersand in front of it) is always wrong.

  13. #13
    Registered User
    Join Date
    May 2010
    Posts
    6

    Got 'cha

    Quote Originally Posted by MK27 View Post
    Yeah, I think what I didn't like about that article was this:



    Which makes me think I am just too lazy to be simple and clear, the next paragraph is kind of dense. On the other hand, it does explain all the & * syntax and nomenclature, and you might as well re-read one paragraph 5 times as read 5 different paragraphs that draw the explanation out further.
    Yea, Cocoa has pointers when creating its objects but they are pretty simple. I thot I could use my knowledge on them within the C language, but from this post you can see that C has a little more to them.

    I have your page bookmarked, and will keep checking back for when I get post with all the *, **, *'s. Cheers mate!

    Quote Originally Posted by tabstop View Post
    Can't be done. That's what it means by "decaying to a pointer" -- there is no way to get an array into a function -- either you pass a specific element of the array to the function (which can be done either by value or by reference) or you type the array name and it decays to a pointer to the first element. Passing the address of an array (as in with an ampersand in front of it) is always wrong.
    I think I get it now, because its already a pointer (kinda) you just have to pass the variable as it is, otherwise if it was a pointer-to-pointer-array-of-chars then my function declaration would need to change to char **.

    I guess I just need to keep practicing with them, like I said the program works fine and now that I know the concept is correct I can keep using this concept within future coding.

    Thanks again for your wisdom!!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Good architecture/design needed for "sample pipeline"
    By johny145 in forum C++ Programming
    Replies: 2
    Last Post: 10-10-2005, 10:43 AM
  2. Conversion with a twist (help needed)
    By Mosquito in forum C++ Programming
    Replies: 7
    Last Post: 06-09-2005, 10:14 PM
  3. Direct3D help needed
    By kawk in forum Game Programming
    Replies: 1
    Last Post: 02-28-2005, 07:13 PM
  4. How good do you have to be to get into the business?
    By SmashBro in forum Game Programming
    Replies: 13
    Last Post: 12-16-2002, 01:36 AM
  5. Guidance needed!!
    By pdstatha in forum C++ Programming
    Replies: 2
    Last Post: 06-22-2002, 09:22 AM

Tags for this Thread