Unable to pin down a bug in a simple multithreading program - c

I am working on a project that involves multi-threading. Although I have a decent understanding of multi-threading, I have not written many such codes. The following code is just a simple code that I wrote for hands-on. It works fine when compiled with gcc -pthread.
To build upon this code, I need to include some libraries that already have pthread included and linked. If I compile by including and linking those libraries, 3 out of 5 times it gives segmentation fault. There is some problem with the first for-loop in main() -- replacing this for-loop with multiple statements does the work.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 3
pthread_mutex_t m_lock = PTHREAD_MUTEX_INITIALIZER;
typedef struct{
int id;
char ip[20];
} thread_data;
void *doOperation(void* ctx)
{
pthread_mutex_lock(&m_lock);
thread_data *m_ctx = (thread_data *)ctx;
printf("Reached here\n");
pthread_mutex_unlock(&m_lock);
pthread_exit(NULL);
}
int main()
{
thread_data ctx[NUM_THREADS];
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i)
{
char ip_n[] = "127.0.0.";
char ip_h[4];
sprintf(ip_h, "%d", i+1);
strcpy(ctx[i].ip, strcat(ip_n, ip_h));
}
for (int i = 0; i < NUM_THREADS; ++i)
{
pthread_create(&threads[i], NULL, doOperation, (void *)&ctx[i])
}
for (int i = 0; i < NUM_THREADS; ++i)
{
pthread_join(threads[i], NULL);
}
pthread_exit(NULL);
}

You say the code you present "works fine", but it is buggy. In particular, the first for loop is buggy, so it is not surprising that it gives you trouble under some circumstances. Here's a breakdown:
char ip_n[] = "127.0.0.";
You have declared ip_n as an array of char exactly long enough to hold the given initializer, including its terminating null character.
char ip_h[4];
sprintf(ip_h, "%d", i+1);
Supposing that the sprintf() succeeds, you have written a non-empty string into char array ip_h.
strcpy(ctx[i].ip, strcat(ip_n, ip_h));
You attempt via strcat() to append the contents of ip_h to the end of ip_n, but there is no room -- this writes past the bounds of ip_n, producing undefined behavior.
The easiest way to solve this would probably be to declare ip_n with an explicit length that is sufficient for the full data. In general, a dotted-quad IP address string might require as many as 16 bytes, including the terminator:
char ip_n[16] = "127.0.0.";

You can't strcat(ip_n, ip_h) because the array ip_n is only big enough to hold the string "127.0.0.". Here's what the man page says, emphasis added
The strcat() and strncat() functions append a copy of the
null-terminated string s2 to the end of the null-terminated string
s1, then add a terminating `\0'. The string s1 must have sufficient
space to hold the result.
The declaration should be
char ip_n[20] = "127.0.0.";

I just added ; at the end of pthread_create(&threads[i], NULL, doOperation, (void *)&ctx[i])
And this segmentation fault might be because of
char ip_n[] = "127.0.0.";
Above, the sizeof(ip_n) only returns 9. But you need atleast 10 characters to store string like 127.0.0.3 (Including null character at the end). Unauthorized memory access might result in segmentation fault. Try replacing this with char ip_n[10] = "127.0.0.";

ip_n[] points to a constant; the compiler reserved 9 bytes including the NULL byte. Anything after these 9 bytes should not be accessed and if you do, the result is undefined (it could work some of the time but likely not all the time). When you do:
strcat(ip_n, ip_h)
You are overflowing the buffer pointed to by ip_n. Maybe this is what is causing your problem. If it's not, I still recommend fixing this.

Related

Segfault after strsep only when compiling with clang 10

I am writing a parser (for NMEA sentences) which splits a string on commas using strsep. When compiled with clang (Apple LLVM version 10.0.1), the code segfaults when splitting a string which has an even number of tokens. When compiled with clang (version 7.0.1) or gcc (9.1.1) on Linux the code works correctly.
A stripped down version of the code which exhibits the issue is as follows:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
static void gnss_parse_gsa (uint8_t argc, char **argv)
{
}
/**
* Desciptor for a NMEA sentence parser
*/
struct gps_parser_t {
void (*parse)(uint8_t, char**);
const char *type;
};
/**
* List of avaliable NMEA sentence parsers
*/
static const struct gps_parser_t nmea_parsers[] = {
{.parse = gnss_parse_gsa, .type = "GPGSA"}
};
static void gnss_line_callback (char *line)
{
/* Count the number of comma seperated tokens in the line */
uint8_t num_args = 1;
for (uint16_t i = 0; i < strlen(line); i++) {
num_args += (line[i] == ',');
}
/* Tokenize the sentence */
char *args[num_args];
for (uint16_t i = 0; (args[i] = strsep(&line, ",")) != NULL; i++);
/* Run parser for received sentence */
uint8_t num_parsers = sizeof(nmea_parsers)/sizeof(nmea_parsers[0]);
for (int i = 0; i < num_parsers; i++) {
if (!strcasecmp(args[0] + 1, nmea_parsers[i].type)) {
nmea_parsers[i].parse(num_args, args);
break;
}
}
}
int main (int argc, char **argv)
{
char pgsa_str[] = "$GPGSA,A,3,02,12,17,03,19,23,06,,,,,,1.41,1.13,0.85*03";
gnss_line_callback(pgsa_str);
}
The segfault occurs at on the line if (!strcasecmp(args[0] + 1, nmea_parsers[i].type)) {, the index operation on args attempts to deference a null pointer.
Increasing the size of the stack, either by manually editing the assembly or adding a call to printf("") anywhere in the function makes it no longer segfault, as does making the args array bigger (eg. adding one to num_args).
In summary, any of the following items prevent the segfault:
- Using a compiler other than clang 10
- Modifying the assembly to make the stack size before dynamic allocation 80 bytes or more (compiles to 64)
- Using an input string with an odd number of tokens
- Allocating args as a fixed length array with the correct number of tokens (or more)
- Allocating args as a variable length array with at least num_args + 1 elements
Note that when compiled with clang 7 on Linux the stack size before dynamic allocation is still 64 bytes, but the code does not segfault.
I'm hoping that someone might be able to explain why this happens, and if there is any way I can get this code to compile correctly with clang 10.
When all sorts of barely-relevant factors like the specific version of the compiler seem to make a difference, it's a pretty sure sign you've got undefined behavior somewhere.
You correctly count the commas to predetermine the exact number of fields, num_args. You allocate an array just barely big enough to hold those fields:
char *args[num_args];
But then you run this loop:
for (uint16_t i = 0; (args[i] = strsep(&line, ",")) != NULL; i++);
There are going to be num_args number of trips through this loop where strsep returns non-NULL pointers that get filled in to args[0] through args[num_args-1], which is what you intended, and which is fine. But then there's one more call to strsep, the one that returns NULL and terminates the loop -- but that null pointer also gets stored into the args array also, specifically into args[num_args], which is one cell off the end. Array overflow, in other words.
There are two ways to fix this. You can use an additional variable so you can capture and test strsep's return value before storing it into the args array:
char *p;
for (uint16_t i = 0; (p = strsep(&line, ",")) != NULL; i++)
args[i] = p;
This also has the side benefit that you have a more conventional loop, with an actual body.
Or, you can declare the args array one bigger than it strictly has to be, meaning that it's got room for that last, NULL pointer stored in args[num_args]:
char *args[num_args+1];
This has the side benefit that you always pass a "NULL terminated array" to the parsing functions, which can be handy for them (and which ends up matching, as it happens, the way main gets called).

Copying strings from extern char environ in C

I have a question pertaining to the extern char **environ. I'm trying to make a C program that counts the size of the environ list, copies it to an array of strings (array of array of chars), and then sorts it alphabetically with a bubble sort. It will print in name=value or value=name order depending on the format value.
I tried using strncpy to get the strings from environ to my new array, but the string values come out empty. I suspect I'm trying to use environ in a way I can't, so I'm looking for help. I've tried to look online for help, but this particular program is very limited. I cannot use system(), yet the only help I've found online tells me to make a program to make this system call. (This does not help).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[])
{
char **env = environ;
int i = 0;
int j = 0;
printf("Hello world!\n");
int listSZ = 0;
char temp[1024];
while(env[listSZ])
{
listSZ++;
}
printf("DEBUG: LIST SIZE = %d\n", listSZ);
char **list = malloc(listSZ * sizeof(char**));
char **sorted = malloc(listSZ * sizeof(char**));
for(i = 0; i < listSZ; i++)
{
list[i] = malloc(sizeof(env[i]) * sizeof(char)); // set the 2D Array strings to size 80, for good measure
sorted[i] = malloc(sizeof(env[i]) * sizeof(char));
}
while(env[i])
{
strncpy(list[i], env[i], sizeof(env[i]));
i++;
} // copy is empty???
for(i = 0; i < listSZ - 1; i++)
{
for(j = 0; j < sizeof(list[i]); j++)
{
if(list[i][j] > list[i+1][j])
{
strcpy(temp, list[i]);
strcpy(list[i], list[i+1]);
strcpy(list[i+1], temp);
j = sizeof(list[i]); // end loop, we resolved this specific entry
}
// else continue
}
}
This is my code, help is greatly appreciated. Why is this such a hard to find topic? Is it the lack of necessity?
EDIT: Pasted wrong code, this was a separate .c file on the same topic, but I started fresh on another file.
In a unix environment, the environment is a third parameter to main.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[], char **envp)
{
while (*envp) {
printf("%s\n", *envp);
*envp++;
}
}
There are multiple problems with your code, including:
Allocating the 'wrong' size for list and sorted (you multiply by sizeof(char **), but should be multiplying by sizeof(char *) because you're allocating an array of char *. This bug won't actually hurt you this time. Using sizeof(*list) avoids the problem.
Allocating the wrong size for the elements in list and sorted. You need to use strlen(env[i]) + 1 for the size, remembering to allow for the null that terminates the string.
You don't check the memory allocations.
Your string copying loop is using strncpy() and shouldn't (actually, you should seldom use strncpy()), not least because it is only copying 4 or 8 bytes of each environment variable (depending on whether you're on a 32-bit or 64-bit system), and it is not ensuring that they're null terminated strings (just one of the many reasons for not using strncpy().
Your outer loop of your 'sorting' code is OK; your inner loop is 100% bogus because you should be using the length of one or the other string, not the size of the pointer, and your comparisons are on single characters, but you're then using strcpy() where you simply need to move pointers around.
You allocate but don't use sorted.
You don't print the sorted environment to demonstrate that it is sorted.
Your code is missing the final }.
Here is some simple code that uses the standard C library qsort() function to do the sorting, and simulates POSIX strdup()
under the name dup_str() — you could use strdup() if you have POSIX available to you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
/* Can also be spelled strdup() and provided by the system */
static char *dup_str(const char *str)
{
size_t len = strlen(str) + 1;
char *dup = malloc(len);
if (dup != NULL)
memmove(dup, str, len);
return dup;
}
static int cmp_str(const void *v1, const void *v2)
{
const char *s1 = *(const char **)v1;
const char *s2 = *(const char **)v2;
return strcmp(s1, s2);
}
int main(void)
{
char **env = environ;
int listSZ;
for (listSZ = 0; env[listSZ] != NULL; listSZ++)
;
printf("DEBUG: Number of environment variables = %d\n", listSZ);
char **list = malloc(listSZ * sizeof(*list));
if (list == NULL)
{
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < listSZ; i++)
{
if ((list[i] = dup_str(env[i])) == NULL)
{
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
}
qsort(list, listSZ, sizeof(list[0]), cmp_str);
for (int i = 0; i < listSZ; i++)
printf("%2d: %s\n", i, list[i]);
return 0;
}
Other people pointed out that you can get at the environment via a third argument to main(), using the prototype int main(int argc, char **argv, char **envp). Note that Microsoft explicitly supports this. They're correct, but you can also get at the environment via environ, even in functions other than main(). The variable environ is unique amongst the global variables defined by POSIX in not being declared in any header file, so you must write the declaration yourself.
Note that the memory allocation is error checked and the error reported on standard error, not standard output.
Clearly, if you like writing and debugging sort algorithms, you can avoid using qsort(). Note that string comparisons need to be done using strcmp(), but you can't use strcmp() directly with qsort() when you're sorting an array of pointers because the argument types are wrong.
Part of the output for me was:
DEBUG: Number of environment variables = 51
0: Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.tQHOVHUgys/Render
1: BASH_ENV=/Users/jleffler/.bashrc
2: CDPATH=:/Users/jleffler:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work:/Users/jleffler/soq/src
3: CLICOLOR=1
4: DBDATE=Y4MD-
…
47: VISUAL=vim
48: XPC_FLAGS=0x0
49: XPC_SERVICE_NAME=0
50: _=./pe17
If you want to sort the values instead of the names, you have to do some harder work. You'd need to define what output you wish to see. There are multiple ways of handling that sort.
To get the environment variables, you need to declare main like this:
int main(int argc, char **argv, char **env);
The third parameter is the NULL-terminated list of environment variables. See:
#include <stdio.h>
int main(int argc, char **argv, char **environ)
{
for(size_t i = 0; env[i]; ++i)
puts(environ[i]);
return 0;
}
The output of this is:
LD_LIBRARY_PATH=/home/shaoran/opt/node-v6.9.4-linux-x64/lib:
LS_COLORS=rs=0:di=01;34:ln=01;36:m
...
Note also that sizeof(environ[i]) in your code does not get you the length of
the string, it gets you the size of a pointer, so
strncpy(list[i], environ[i], sizeof(environ[i]));
is wrong. Also the whole point of strncpy is to limit based on the destination,
not on the source, otherwise if the source is larger than the destination, you
will still overflow the buffer. The correct call would be
strncpy(list[i], environ[i], 80);
list[i][79] = 0;
Bare in mind that strncpy might not write the '\0'-terminating byte if the
destination is not large enough, so you have to make sure to terminate the
string. Also note that 79 characters might be too short for storing env variables. For example, my LS_COLORS variable
is huge, at least 1500 characters long. You might want to do your list[i] = malloc calls based based on strlen(environ[i])+1.
Another thing: your swapping
strcpy(temp, list[i]);
strcpy(list[i], list[i+1]);
strcpy(list[i+1], temp);
j = sizeof(list[i]);
works only if all list[i] point to memory of the same size. Since the list[i] are pointers, the cheaper way of swapping would be by
swapping the pointers instead:
char *tmp = list[i];
list[i] = list[i+1];
list[i+1] = tmp;
This is more efficient, is a O(1) operation and you don't have to worry if the
memory spaces are not of the same size.
What I don't get is, what do you intend with j = sizeof(list[i])? Not only
that sizeof(list[i]) returns you the size of a pointer (which will be constant
for all list[i]), why are you messing with the running variable j inside the
block? If you want to leave the loop, the do break. And you are looking for
strlen(list[i]): this will give you the length of the string.

Segmentation fault when trying to declare an array of strings

In my program, I am trying to copy each argv[i] to keyword[i], but my program fails with a segmentation fault. What am I doing wrong?
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
//prototype
string keyword = "";
//int j;
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
keyword[i] = toupper(argv[1][i]);
printf("%i-- printing letters\n", keyword[i]);
}
}
As others have observed, you initialize variable keyword either as an empty string or as a pointer to an empty string literal, depending on the definition of type string. Either way, it is then valid to evaluate keyword[i] only for i equal to zero; any other value -- for read or write -- is out of bounds. Furthermore, in the latter (pointer to string literal) case, you must not attempt to modify the array keyword points to.
Note in particular that C does not automatically expand strings if you try to access an out of bounds element. Instead, an attempt to do so produces "undefined behavior", and a common way for that to manifest in such cases is in the form of a segmentation fault. You can view a segmentation fault as the system slapping down your program for attempting to access memory that does not belong to it.
Since you don't know a priori how long the argument string will be before you copy it, the most viable type for keyword is char *. I will use that type instead of string in what follows, for clarity.
If you indeed do want to make a copy of the argument, then by far the easiest way to do so is via the for-purpose function strdup():
char *keyword = strdup(argv[1]);
That allocates enough memory for a copy of its argument, including the terminator, copies it, and returns a pointer to the result. You are then obligated to free the resulting memory via the free() function when you're done with it. Having made a copy in that way, you can then upcase each element in place:
for (int i = 0, n = strlen(keyword); i < n; i++)
{
keyword[i] = toupper(keyword[i]);
printf("%c-- printing letters\n", keyword[i]);
}
Note, by the way, that the printf() format descriptor for a single character is %c, not %i. You must use that to print the characters as characters, rather than their integer values.
That's one of the simplest ways to write C code for what you're trying to do, though there are many variations. The only other one I'll offer for your consideration is to not copy the argument at all:
char *keyword = argv[1];
If you initialize keyword that way then you do not allocate any memory or make a copy; instead, you set keyword to point to the same string that argv[1] points to. You can modify that string in-place (though you cannot lengthen it), provided that you do not need to retain its original contents.
Before I wrap this up, I should also observe that your program does not check whether there actually is an argument. In the event that there is not (i.e. argc < 2), argv[1] either contains a null pointer (argc == 1) or is undefined (argc == 0; you're unlikely ever to run into this). Either way, your program produces undefined behavior in that case if it attempts to use argv[1] as if it were a pointer to a valid string. You should test for this case first off, and terminate with a diagnostic message if no program argument is available.
Your main problem: you're not allocating memory for your new string, (string keyword = "").
In C, every size that is not known at compilation time has to be dynamically allocated during run-time.
Also, you never check for missing parameters which may crash your program.
See code below for both fixes
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: %s <word>\n", argv[0]);
return 1;
}
int length = strlen(argv[1]);
string keyword = malloc(length+1);
for(int i = 0, n = strlen(argv[1]); i < n; i++)
{
keyword[i] = toupper(argv[1][i]);
printf("%i-- printing letters\n", keyword[i]);
}
keyword[length]=0;
free(keyword);
}

Why does this c program crashe?

I want to make a list of , for example 10 sentences that are entered through the keyboard. For getting a line I am using a function getline(). Can anybody explain why does this program crash upon entering the second line? Where is the mistake ?
#define LISTMAX 100
#define LINEMAX 100
#include <stdio.h>
#include <string.h>
void getline(char *);
int main ()
{
char w[LINEMAX], *list[LISTMAX];
int i;
for(i = 0; i < 10; i++)
{
getline(w);
strcpy(list[i], w);
}
for(i = 0; i < 10; i++)
printf("%s\n", list[i]);
return 0;
}
void getline(char *word)
{
while((*word++ = getchar()) != '\n');
*word = '\0';
}
A string is a block of memory (an array), which contains chars, terminated by '\0'. A char * is not a string; it's just a pointer to the first char in a string.
strcpy does not create a new string. It just copies the data from one block of memory to another. So your problem is: you haven't allocated a block of memory to hold the string.
I'll show you two solutions. The first solution is: change the declaration of list so that the memory is already allocated. If you do it this way, you can avoid using strcpy, so your code is simpler:
// no need for w
char list[10][LISTMAX];
// ...
// get the line straight into list
// no need to copy strings
getline(list[i]);
But if you want to stretch yourself, the second solution is to allocate the block of memory when you know you'll need it. You need to do this a lot in C, so maybe now is a good time to learn this technique:
#include <stdlib.h> // include the malloc function
// ...
char w[LINEMAX], * list[LISTMAX]
// put this line between the getline and strcpy lines
list[i] = (char *) malloc((strlen(w) + 1) * sizeof(char));
This solution is more complicated, but you only allocate as much memory as you need for the string. If the string is 10 characters long, you only request enough memory to hold 11 characters (10 characters + '\0') from the system. This is important if, say, you want to read in a file, and you've no idea how big the file will be.
By the way, why do you have LINEMAX and LISTMAX as separate constants? Can you think of a reason why they might be different? And why haven't you made 10 a constant? Wouldn't this be better?
#define LINEMAX 100
#define NUMBER_OF_LINES 10
// ...
char list[NUMBER_OF_LINES][LINEMAX];
// ...
for (i = 0; i < NUMBER_OF_LINES; i++)

Segmentation Fault in Simple Offset Encryption

Alright guys, this is my first post here. The most recent assignment in my compsci class has us coding a couple of functions to encode and decode strings based on a simple offset. So far in my encryption function I am trying to convert uppercase alphas in a string to their ASCII equivalent(an int), add the offset(and adjust if the ASCII value goes past 'Z'), cast that int back to a char(the new encrypted char) and put it into a new string. What I have here compiles fine, but it gives a Segmentation Fault (core dumped) error when I run it and input simple uppercase strings. Where am I going wrong here? (NOTE: there are some commented out bits from an attempt at solving the situation that created some odd errors in main)
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <stdlib.h>
char *encrypt(char *str, int offset){
int counter;
char medianstr[strlen(str)];
char *returnstr;// = malloc(sizeof(char) * strlen(str));
for(counter = 0; counter < strlen(str); counter++){
if(isalpha(str[counter]) && isupper(str[counter])){//If the character at current index is an alpha and uppercase
int charASCII = (int)str[counter];//Get ASCII value of character
int newASCII;
if(charASCII+offset <= 90 ){//If the offset won't put it outside of the uppercase range
newASCII = charASCII + offset;//Just add the offset for the new value
medianstr[counter] = (char)newASCII;
}else{
newASCII = 64 + ((charASCII + offset) - 90);//If the offset will put it outside the uppercase range, add the remaining starting at 64(right before A)
medianstr[counter] = (char)newASCII;
}
}
}
strcpy(returnstr, medianstr);
return returnstr;
}
/*
char *decrypt(char *str, int offset){
}
*/
int main(){
char *inputstr;
printf("Please enter the string to be encrypted:");
scanf("%s", inputstr);
char *encryptedstr;
encryptedstr = encrypt(inputstr, 5);
printf("%s", encryptedstr);
//free(encryptedstr);
return 0;
}
You use a bunch of pointers, but never allocate any memory to them. That will lead to segment faults.
Actually the strange thing is it seems you know you need to do this as you have the code in place, but you commented it out:
char *returnstr;// = malloc(sizeof(char) * strlen(str));
When you use a pointer you need to "point" it to allocated memory, it can either point to dynamic memory that you request via malloc() or static memory (such as an array that you declared); when you're done with dynamic memory you need to free() it, but again you seem to know this as you commented out a call to free.
Just a malloc() to inputstr and one for returnstr will be enough to get this working.
Without going any further the segmentation fault comes from your use of scanf().
Segmentation fault occurs at scanf() because it tries to write to *inputstr(a block of location inputstr is pointing at); it isn't allocated at this point.
To invoke scanf() you need to feed in a pointer in whose memory address it points to is allocated first.
Naturally, to fix the segmentation fault you want to well, allocate the memory to your char *inputstr.
To dynamically allocate memory of 128 bytes(i.e., the pointer will point to heap):
char *inputstr = (char *) malloc(128);
Or to statically allocate memory of 128 bytes(i.e., the pointer will point to stack):
char inputstr[128];
There is a lot of complexity in the encrypt() function that isn't really necessary. Note that computing the length of the string on each iteration of the loop is a costly process in general. I noted in a comment:
What's with the 90 and 64? Why not use 'A' and 'Z'? And you've commented out the memory allocation for returnstr, so you're copying via an uninitialized pointer and then returning that? Not a recipe for happiness!
The other answers have also pointed out (accurately) that you've not initialized your pointer in main(), so you don't get a chance to dump core in encrypt() because you've already dumped core in main().
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
char *encrypt(char *str, int offset)
{
int len = strlen(str) + 1;
char *returnstr = malloc(len);
if (returnstr == 0)
return 0;
for (int i = 0; i < len; i++)
{
char c = str[i];
if (isupper((unsigned char)c))
{
c += offset;
if (c > 'Z')
c = 'A' + (c - 'Z') - 1;
}
returnstr[i] = c;
}
return returnstr;
}
Long variable names are not always helpful; they make the code harder to read. Note that any character for which isupper() is true also satisfies isalpha(). The cast on the argument to isupper() prevents problems when the char type is signed and you have data where the unsigned char value is in the range 0x80..0xFF (the high bit is set). With the cast, the code will work correctly; without, you can get into trouble.

Resources