VS 2019 stricmp and _stricmp both cause compiler error - c++

The source code itself isn't really in question. It's the compiler's reaction to it. Here's the troublesome snippet:
int XtnUtil::IsExtension(const char * filename, const char * xtn)
{
char* fx = FindExtension(filename); // Get a pointer to the filename extension
if (!fx) return 0; // Bail out if it's not there
if (xtn[0] == '.') xtn++; // Make sure we're looking at the alpha part
return (stricmp(fx, xtn) ? 0 : 1); // TRUE if they're equal
}
I've also used _stricmp instead of stricmp. In either case, the compiler gives me a particularly uninformative message:
It seems to say "Don't use _stricmp, use _stricmp instead." I tried it with and without the underscore and also tried the POSIX equivalent, strcasecmp() but Visual Studio doesn't seem to know that function at all.
For the moment, I've moved past this by simply writing my own function named mystricmp() which is kind of distasteful but seems to work. Right now I'm mostly interested in why the compiler gave me such a funky message, and what would I be able to do about it if the function I had to hand-write weren't trivial?

Related

std::string constructor with lvalue throws with clang

I am making use of Matei David's handy C++ wrapper for zlib, but I get an error when compiling on macOs (clang-1100.0.33.
include/strict_fstream.hpp:39:37: error: cannot initialize a parameter of type 'const char *' with an lvalue of type 'int'
The problem is here:
/// Overload of error-reporting function, to enable use with VS.
/// Ref: http://stackoverflow.com/a/901316/717706
static std::string strerror()
{
std::string buff(80, '\0');
// options for other environments omitted, where error message is set
// if not Win32 or _POSIX_C_SOURCE >= 200112L, error message is left empty.
auto p = strerror_r(errno, &buff[0], buff.size());
// error next line
std::string tmp(p, std::strlen(p));
std::swap(buff, tmp);
buff.resize(buff.find('\0'));
return buff;
}
(Which IIUC has nothing to do with zlib, just trying to report errors in a thread safe manner).
If I change to this:
static std::string strerror()
{
std::string buff(80, '\0');
auto p = strerror_r(errno, &buff[0], buff.size());
// "fix" below
size_t length = buff.size();
std::string tmp(p, length);
std::swap(buff, tmp);
buff.resize(buff.find('\0'));
return buff;
}
My program compiles and runs fine.
I have two questions:
Why does clang not like the constructor std::string tmp(p, std::strlen(p));?
The buffer was declared at the beginning of the function as length 80. Why are we even bothering to look up the length?
The answer to 2 may answer this, but is there something wrong with my version?
Thanks.
If you use int strerror_r(int errnum, char *buf, size_t buflen);, then there is no appropriate string constructor and the program is ill formed.
If you use char *strerror_r(int errnum, char *buf, size_t buflen);, then the program is well-formed.
The standard C/POSIX library implementation influences which function you get. Compiler is only involved as much as influencing what system library may be used by default.
Former function is extension to POSIX specified in XSI (which is essentially an optional part of POSIX) and latter is a GNU extension.
In case you use glibc (I don't know if that is an option on MacOS), you can control which version you get with macros, although the XSI compliant version is not available in older versions. Its documentation says:
The XSI-compliant version of strerror_r() is provided if:
(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
The buffer was declared at the beginning of the function as length 80. Why are we even bothering to look up the length?
In the construction std::string tmp(p, std::strlen(p));, strlen seems entirely unnecessary to me. std::string tmp(p); is equivalent.
If you don't need thread safety, then the most portable solution is to use std::strerror which is in standard C++:
return std::strerror(errno); // yes, it's this simple
If you do need thread safety, then you could wrap this in a critical section using a mutex.
Note that strerror, the name of your function, is reserved to the language implementation in the global namespace when the standard library is used. The function should be in a namespace, or be renamed.
There are two different versions of the strerror_r that you'll commonly see:
A POSIX-compliant version that always stores the error message in the provided buffer (if it succeeds) and returns an int (0 for success, non-zero for error)
A GNU version that may store the error message in the provided buffer, or maybe not. It returns a char* pointing to the error message, which may point to the user-supplied buffer or may point to some other global static storage.
That strerror function is clearly written to work with the GNU version of strerror_r.
As for your second question, you need the strlen. buff is 80 characters long, but the actual error message may be shorter and only partially fill the buffer. That strlen is being used to trim off any extra nul characters from the end.

Qt Program crashes with another compiler

Could anyone tell me what's wrong with this part of my code? It crashes during execution.
void MainWindow::on_pushButton_clicked()
{
char * cstr = new char [ui->lineEdit->text().length()];
string costam;
costam=ui->lineEdit->text().toStdString();
strcpy(cstr, costam.c_str()); <<<----TROUBLE LINE
int z;
z=costam.length();
for(int n=0;n<z;n++){
string wynik;
wynik=konwersja(cstr[n]);
mors(wynik);
Sleep(300);
}
delete[] cstr;
}
When I try to compile it with MinGW in Qt 5.0.1 everything is OK but with MSVC2010 in Qt 4.8.1 there is a warning:
warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
Your question is wrong. This code compiles and there's nothing about QT here.
strcpy is deprecated because it's unsafe. Alternative is strcpy_s:
strcpy_s( cstr, ui->lineEdit->text().length() + 1, costam.c_str() );
Note that you should allocate ui->lineEdit->text().length() + 1, not ui->lineEdit->text().length(). That's the reason of crash, I guess.
BTW I see no reason to use cstr array in your code at all. As example:
void MainWindow::on_pushButton_clicked() {
string costam;
costam = ui->lineEdit->text().toStdString();
for( size_t n = 0; n < costam.length(); n++ ) {
string wynik;
wynik = konwersja( costam[ n ] );
mors( wynik );
Sleep( 300 );
}
}
Because people are not very aware of security when programming C++, and Windows gets a bad rap because of it, Visual Studio "deprecated" several functions which are common causes of buffer overflows. In this case you should be fine, and can probably just disable the warning by defining _CRT_SECURE_NO_WARNINGS. You may also find this issue with posix functions, too, in which case you would be able to disable those warnings with a separate #define.
Can you change this line strcpy(cstr, costam.c_str()); with strcpy_s(cstr, costam.c_str()); try compiling again?
Also it shouldn't prevent compiling, MSVC2010 is just warning about an unsafe usage. You can also lower the warning level of MSVC2010.
You have two problems here.
The crash is because strcpy will write length + 1 characters into the destination buffer, but your buffer is only of size length. The +1 is for the null termination character, which is not included in length.
The warning is because Microsoft believes its too easy to make mistakes using strcpy and discourages its use. As Joel mentioned, you could enable a define to prevent that warning. I would not recommend semihyagcioglu and Microsoft's suggestion of using strcpy_s, as it's not a portable solution.
I'd also like to note that while fixing those things will make your code compile and run without error, there are other questions. Like: why do you need the cstr variable in the first place? cstr[n] can probably be replaced with costam.data()[n]. Then the cstr variable won't need to exist at all. You won't need the new, delete or strcpy.

Optimization makes function return immediately instead of executing

I'm working on VS 2010 express and trying to do some file reading and parsing work
my function goes something like this... (I dropped the boring parts)
void SomeClass::SomeFunc(char *ALName, std::map<...> *chromList, std::map<...> *chromLine)
{
ifstream file;
char tmpBuff[500];
char tmpBuff2[500];
char fileName[350];
char tmp[350];
char *pch;
char *pch2;
.....
file.open(fileName);
while ( file.getline( tmpBuff, 500 ) )
{
....
if ( some_condition == 0 )
{
pch2 = strtok( NULL, "," );
pch = strtok( NULL, "," );
(*chromList)[pch2] = do_some_work( atoi(pch), tmpBuff2 );
strcpy( tmp, get_chrom_line( tmpBuff2 ) );
(*chromLine)[pch2] = tmp;
}
}
file.close();
}
When I change to Release with Optimization set to Maximum speed this function is skipped.
The debugger enters the function and immediately returns.
When i run with Debug setting or Release with the Optimization flag set to disabled, the function runs just fine.
What can be the possible reason for that?
Can I put a preprocessor definition to force "No optimization" on this function while the rest of the code get optimized
Thanks!!
Idan
You should never try to debug optimized code. The line numbers it shows you will rarely match up with what is actually being executed, and forget about reading local variables. That is why there is a "Debug" and "Release" mode.
However, if you really want to, try this to make Visual Studio not optimize that function. You could also put that function in a separate source file and compile it in debug mode.
Are you sure that the function is actually being skipped and that the debugger isn't simply making it look skipped?
If it really isn't being executed then it's almost certain you have undefined behavior that happens to work how you want it when not optimized and the compiler (probably rightfully) optimizes it into non-working code in optimized mode.
That said I see some questionable items: You call strtok with NULL (aside - prefer 0 or nullptr in C++/C++11) without calling it with a valid pointer. Are you aware that strtok is destructive?
Then you use a character pointer to index an array, or presumably (hopefully?) call an overloaded operator[] function with a char* argument.
Since this is C++, your code will be cleaner, safer, and probably easier to debug if you remove all the C-style parsing and do it with string and find/find_first_of/etc depending on your needs.

C++ std::string alternative to strcpy?

I know there is a similarly titled question already on SO but I want to know my options for this specific case.
MSVC compiler gives a warning about strcpy:
1>c:\something\mycontrol.cpp(65): warning C4996: 'strcpy': This function or
variable may be unsafe. Consider using strcpy_s instead. To disable
deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
Here's my code:
void MyControl::SetFontFace(const char *faceName)
{
LOGFONT lf;
CFont *currentFont = GetFont();
currentFont->GetLogFont(&lf);
strcpy(lf.lfFaceName, faceName); <--- offending line
font_.DeleteObject();
// Create the font.
font_.CreateFontIndirect(&lf);
// Use the font to paint a control.
SetFont(&font_);
}
Note font_ is an instance variable. LOGFONT is a windows structure where lfFaceName is defined as TCHAR lfFaceName[LF_FACESIZE].
What I'm wondering is can I do something like the following (and if not why not):
void MyControl::SetFontFace(const std::string& faceName)
...
lf.lfFaceName = faceName.c_str();
...
Or if there is a different alternative altogether then let me know.
The reason you're getting the security warning is, your faceName argument could point to a string that is longer than LF_FACESIZE characters, and then strcpy would blindly overwrite whatever comes after lfFaceName in the LOGFONT structure. You do have a bug.
You should not blindly fix the bug by changing strcpy to strcpy_s, because:
The *_s functions are unportable Microsoft inventions almost all of which duplicate the functionality of other C library functions that are portable. They should never be used, even in a program not intended to be portable (as this appears to be).
Blind changes tend to not actually fix this class of bug. For instance, the "safe" variants of strcpy (strncpy, strlcpy, strcpy_s) simply truncate the string if it's too long, which in this case would make you try to load the wrong font. Worse, strncpy omits the NUL terminator when it does that, so you'd probably just move the crash inside CreateFontIndirect if you used that one. The correct fix is to check the length up front and fail the entire operation if it's too long. At which point strcpy becomes safe (because you know it's not too long), although I prefer memcpy because it makes it obvious to future readers of the code that I've thought about this.
TCHAR and char are not the same thing; copying either a C-style const char * string or a C++ std::string into an array of TCHAR without a proper encoding conversion may produce complete nonsense. (Using TCHAR is, in my experience, always a mistake, and the biggest problem with it is that code like this will appear to work correctly in an ASCII build, and will still compile in UNICODE mode, but will then fail catastrophically at runtime.)
You certainly can use std::string to help with this problem, but it won't get you out of needing to check the length and manually copy the string. I'd probably do it like this. Note that I am using LOGFONTW and CreateFontIndirectW and an explicit conversion from UTF-8 in the std::string. Note also that chunks of this were cargo-culted out of MSDN and none of it has been tested. Sorry.
void MyControl::SetFontFace(const std::string& faceName)
{
LOGFONTW lf;
this->font_.GetLogFontW(&lf);
int count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
faceName.data(), faceName.length(),
lf.lfFaceName, LF_FACESIZE - 1)
if (count <= 0)
throw GetLastError(); // FIXME: use a real exception
lf.lfFaceName[count] = L'\0'; // MultiByteToWideChar does not NUL-terminate.
this->font_.DeleteObject();
if (!this->font_.CreateFontIndirectW(&lf))
throw GetLastError(); // FIXME: use a real exception
// ...
}
lf.lfFaceName = faceName.c_str();
No you shouldn't do that because you are making a local copy of the poitner to the data held inside the std::string. If the c++ string changes, or is deleted, the pointer is no longer valid, and if lFaceName decides to change the data this will almost certainly break the std::string.
Since you need to copy a c string, you need a 'c' function, then strcpy_s (or it's equivalent) is the safe alternative
Have you tried? Given the information in your post, the assignment should generate a compiler error because you're trying to assign a pointer to an array, which does not work in C(++).
#include <cstdio>
#include <string>
using namespace std;
struct LOGFONT {
char lfFaceName[3];
};
int main() {
struct LOGFONT f;
string foo="bar";
f.lfFaceName = foo.c_str();
return 0;
}
leads to
x.c:13: error: incompatible types in assignment of `const char*' to `char[3]'
I'd recommend using a secure strcpy alternative like the warning says, given that you know the size of the destination space anyway.
#include <algorithm>
#include <iostream>
#include <string>
enum { LF_FACESIZE = 256 }; // = 3 // test too-long input
struct LOGFONT
{
char lfFaceName[LF_FACESIZE];
};
int main()
{
LOGFONT f;
std::string foo("Sans-Serif");
std::copy_n(foo.c_str(), foo.size()+1 > LF_FACESIZE ? LF_FACESIZE : foo.size()+1,
f.lfFaceName);
std::cout << f.lfFaceName << std::endl;
return 0;
}
lf.lfFaceName = faceName.c_str(); won't work for two reasons (assuming you change faceName to a std:string)
The lifetime of the pointer returned by c_str() is temporary. It's only valid as long as the fileName object doesn't change and in alive.
The line won't compile. .c_str() returns a pointer to a char, and lfFaceName is a character array and can't be assigned to. You need to do something to fill in the string array, to fill in the bytes at lfFaceName, and pointer assignment doesn't do that.
There isn't anything C++ that can help here, since lfFaceName is a C "string". You need to use a C string function, like strcpy or strcpy_s. You can change your code to:
strcpy_s(lf.lfFaceName, LF_FACESIZE, faceName);

sprintf_s crashes

I am getting a crash while executing the following code ocassionally at sprintf_s. This code was working many years without any problems. When I gave the size in strcat_s and sprintf_s as in the statements below, the crash is not appearing. What could be the reason for this?
strcat_s(sztmpCurrDate,100,sztmpCurrTime);
sprintf_s(sztmpCurrDate,100,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds););
char sztmpCurrDate[100] = "";
char sztmpCurrTime[100] = "";
SYSTEMTIME curTime;
GetLocalTime(&curTime);
GetLocalTime(&curTime);
GetDateFormat(LOCALE_USER_DEFAULT,
DATE_SHORTDATE,
&curTime,
NULL,
sztmpCurrDate,
100);
GetTimeFormat(LOCALE_USER_DEFAULT,
TIME_FORCE24HOURFORMAT,
&curTime,
"HH':'mm':'ss",
sztmpCurrTime,
100);
strcat_s(sztmpCurrDate," ");
strcat_s(sztmpCurrDate,sztmpCurrTime);
sprintf_s(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
From the documentation for sprintf_s:
If copying occurs between strings that overlap, the behavior is undefined.
Your code:
sprintf_s(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
copies from the source to the destination sztmpCurrDate. Also, you haven't specified the size of the destination string, which is required by sprintf_s (I don't know how your code even compiled like that). Try:
sprintf_s(sztmpCurrDate + strlen(sztmpCurrDate), 100-strlen(sztmpCurrDate),
":%0.3d",curTime.wMilliseconds);
A better approach, since you're using C++, is to use std::string and then you won't have to worry about this sort of C string manipulation error.
Wrong syntax!!!
Second argument of sprintf_s is length of your buffer and in your case when program crashes you provided pointer to C string (char *). This is absolutely syntax error.
The compiler probably issued a warning, but sadly has let this pass. This is because sprintf_s takes three arguments + variable number of arguments. In your wrong case you provided three arguments so the compiler was satisfied, but he treated your "format string" as "number of arguments" and sztmpCurrDate as "format string".
If you used any other function with fixed number of arguments and you provided less than needed this would be a compile error.
You are probably using the incorrect version:
sprintf_s(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
Instead it should be:
int slen = strlen(sztmpCurrDate) + 1;
sprintf_s(sztmpCurrDate, slen, "%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
Refer to this sprintf_s for more information.
The code worked correctly, the code is not correct.
sprintf(sztmpCurrDate,"%s:%0.3d",sztmpCurrDate,curTime.wMilliseconds);
Rewrite as
sprintf(&sztmpCurrDate[strlen(sztmpCurrDate)],"%0.3d",curTime.wMilliseconds);
And for home work tell us why .....